Compare commits

..
Author SHA1 Message Date
James Jia - Test 8dfe36998d update LICENSE 2023-08-31 17:55:45 -07:00
aleka 30187ef69f add new optimized market icons (#15)
* add new optimized market icons
2023-08-31 17:43:25 -04:00
Jared Vu 7d1b9eb65f Adopt v4-localization STRING_KEYS and translation files (#13)
* Delete old localization files

* nits

* @dydxprotocol/v4-localization: 0.0.21

* TAKE_PROFIT -> TAKE_PROFIT_LIMIT
2023-08-31 10:50:41 -07:00
aleka 1835b42bb0 ChainQueriesProtocol -> ChainTransactionsProtocol (#14) 2023-08-31 13:35:51 -04:00
aleka 37a50def99 AssetIcon .svg -> .png (#11) 2023-08-30 18:28:30 -04:00
Jared Vu a97f296905 Utilize v4-localization for Environment Endpoints (#7)
* add abacusFileSystem protocol

* Use v4-localization endpoints, replace old usage of CLIENT_NETWORK_CONFIGS

* remove squidRouter switchNetwork calls, bump abacus

* use ENDPOINTS_PATH to keep abacus/fileSystem consistent

* use NETWORK_ENDPOINTS const

* @dydxprotocol/v4-client -> @dydxprotocol/v4-client-js

* use v4-client-js

* WithdrawForm: Use v4-client-js

* Fix undefined Symbol

* Bump v4-localization
2023-08-30 15:12:40 -04:00
Rui 683d806f44 Add asset PNGs of 128x128 (#10) 2023-08-30 11:23:01 -07:00
Bill e635778438 Deposit/Withdraw with abacus (#3)
* Deposit/Withdraw with abacus

* revert package

* e2e working deposit/withdraws

* deprecate usesquidrouter

* remove unused types

* fix chain select menu

* fix switch network bug

* working switch network deposits

* fix withdraw chain select

* address comments

* address comments

* address comments

* fix clear bug

* use diffarrow

* address nits
2023-08-29 12:11:20 -07:00
aleka 7a456b33d4 Merge pull request #6 from dydxprotocol/readme-vercel-setup
Add instructions for deploying with vercel in readme
2023-08-29 13:07:21 -04:00
aleka 6d25a072bd Add instructions for deploying with vercel in readme 2023-08-28 17:51:03 -04:00
Jared Vu d6baa6ef3f Merge pull request #5 from dydxprotocol/conditional-orders
Conditional orders
2023-08-28 05:49:27 -10:00
jaredvu 195ddd5af7 Move localeProvider, add label, valid marketId 2023-08-28 08:43:36 -07:00
aleka 2a5373c972 Merge pull request #4 from dydxprotocol/transfers-history-table
[TRCL-1842] Transfers history table
2023-08-25 14:13:53 -04:00
Aleka Cheung 64f663ed5b add CopyButton component 2023-08-25 14:07:03 -04:00
jaredvu 983128fd74 misc fix/cleanup 2023-08-25 10:59:59 -07:00
jaredvu e15f4b919c useCurrentMarketId hook for navigation 2023-08-25 10:59:35 -07:00
jaredvu 71c29524b1 useMarketsData: Filter out test-usd market 2023-08-24 14:23:02 -07:00
jaredvu 0e26433e29 disable conditional orders on testnet until they support them 2023-08-24 14:18:55 -07:00
jaredvu 3afd5ef783 OrdersTable: Minimum stepSizeDecimals = TOKEN_DECIMALS 2023-08-24 13:35:38 -07:00
jaredvu b2cac81235 SearchSelect: DropdownIcon position 2023-08-24 13:27:40 -07:00
jaredvu e48ec4ca73 Fix Orderbook spread when nothing on the books 2023-08-24 12:06:03 -07:00
jaredvu 78f9ddff7a Fix crash on test coin 2023-08-24 12:05:47 -07:00
jaredvu c1dcca43c8 Render TradeBoxOrderView from Abacus typeOptions 2023-08-23 23:32:02 -07:00
jaredvu 029476fa19 fix type for placeOrder analytics 2023-08-23 17:31:14 -07:00
jaredvu 6ecdcd981e enable conditional orders 2023-08-23 17:18:40 -07:00
jaredvu 591166e93d locale/browserTag 2023-08-23 17:18:10 -07:00
Aleka Cheung e30940e7df bump abacus + make addresses copyable 2023-08-23 19:37:19 -04:00
Aleka Cheung 2d0a944097 transfers history table 2023-08-23 19:37:14 -04:00
aleka 0dd2ecb6d2 Merge pull request #1 from dydxprotocol/receive-transfer-entry-in-account-menu
Add receive/transfer dialog entry points in AccountMenu + dv4tnt renames
2023-08-23 18:57:32 -04:00
Aleka Cheung 76819e0839 signed commits 2023-08-23 18:56:12 -04:00
Aleka Cheung 32b375cfa4 address feedback 2023-08-23 18:56:11 -04:00
Aleka Cheung 3dc87e873d show receive button for both assets 2023-08-23 18:56:10 -04:00
Aleka Cheung ed61b3112e rename tokens to dv4tnt, fix header dropdown hover state, add entry points to receive/send dialogs in account menu 2023-08-23 18:56:08 -04:00
Jared Vu 42f4142307 Merge pull request #2 from dydxprotocol/analytics-performance
Analytics: add performance metrics
2023-08-21 10:00:00 -10:00
Darryl Yeo c5e79980d1 Analytics: add performance metrics 2023-08-18 13:02:25 -07:00
James Jia - Test 6e179191d8 Initial commit 2023-08-17 15:52:35 -07:00
382 changed files with 7684 additions and 66960 deletions
+18
View File
@@ -0,0 +1,18 @@
VITE_BASE_URL=https://v4.testnet.dydx.exchange
VITE_ALCHEMY_API_KEY=FP275q327Yh7qswtZWenbPXdZZdnAFmC
VITE_FAUCET_URL_DEV=https://faucet.v4dev.dydx.exchange/
VITE_FAUCET_URL_STAGE=https://faucet.v4staging.dydx.exchange/
VITE_FAUCET_URL_TESTNET2=https://faucet.v4testnet2.dydx.exchange/
VITE_VALIDATOR_URL_DEV=http://52.192.187.113:26657/
VITE_VALIDATOR_URL_STAGE=https://validator.v4staging.dydx.exchange/
VITE_VALIDATOR_URL_TESTNET2=https://validator.v4testnet2.dydx.exchange/
VITE_INDEXER_URL_DEV=http://dev-indexer-apne1-lb-public-890774175.ap-northeast-1.elb.amazonaws.com
VITE_INDEXER_URL_STAGE=https://indexer.v4staging.dydx.exchange
VITE_INDEXER_URL_TESTNET2=https://indexer.v4testnet2.dydx.exchange
VITE_WALLETCONNECT1_BRIDGE=wss://api.dydx.exchange/wc/
VITE_WALLETCONNECT2_PROJECT_ID=fbe94eaa691fa8d929561f8567062b32
+11 -8
View File
@@ -1,14 +1,17 @@
VITE_BASE_URL=
VITE_ALCHEMY_API_KEY=
VITE_PK_ENCRYPTION_KEY=
VITE_WALLETCONNECT2_PROJECT_ID=
VITE_V3_TOKEN_ADDRESS=
VITE_TOKEN_MIGRATION_URI=
VITE_FAUCET_URL_DEV=
VITE_FAUCET_URL_STAGE=
VITE_FAUCET_URL_TESTNET2=
AMPLITUDE_API_KEY=
BUGSNAG_API_KEY=
IOS_APP_ID=
INTERCOM_APP_ID=
STATUS_PAGE_SCRIPT_URI=
VITE_VALIDATOR_URL_DEV=
VITE_VALIDATOR_URL_STAGE=
VITE_VALIDATOR_URL_TESTNET2=
VITE_INDEXER_URL_DEV=
VITE_INDEXER_URL_STAGE=
VITE_INDEXER_URL_TESTNET2=
+5
View File
@@ -5,10 +5,15 @@ vite.config.ts
# Temporarily ignore, we will slowly remove each directory as we fix files to follow ESLint rules
/src/components
/src/dialogs
/src/forms
/src/hooks
/src/history.ts
/src/icons
/src/lib
/src/main.tsx
/src/menus
/src/pages
/src/state
/src/styles
/src/views
+6 -4
View File
@@ -33,11 +33,14 @@
"jsx-a11y/anchor-is-valid": "off",
"jsx-a11y/click-events-have-key-events": "off",
"no-continue": "off",
"no-console": ["error"],
"no-console": [
"error",
{
"devDependencies": ["./scripts/*.js"]
}
],
"no-lonely-if": "off",
"no-multi-assign": "off",
"no-nested-ternary": "off",
"no-param-reassign": ["error", { "props": false }],
"no-return-assign": "off",
"no-return-await": "off",
"no-underscore-dangle": "off",
@@ -62,7 +65,6 @@
"react/sort-comp": "off",
"@typescript-eslint/no-use-before-define": "off",
"@typescript-eslint/comma-dangle": "off",
"@typescript-eslint/no-unused-vars": "error",
"@typescript-eslint/member-delimiter-style": [
"error",
{
-2
View File
@@ -33,13 +33,11 @@ jobs:
AMPLITUDE_API_KEY: ${{ secrets.AMPLITUDE_API_KEY }}
BUGSNAG_API_KEY: ${{ secrets.BUGSNAG_API_KEY }}
STATUS_PAGE_SCRIPT_URI: ${{ secrets.STATUS_PAGE_SCRIPT_URI }}
IOS_APP_ID: ${{ secrets.IOS_APP_ID }}
run: |
pnpm run build
pnpm run build:inject-amplitude
pnpm run build:inject-bugsnag
pnpm run build:inject-statuspage
sh scripts/inject-app-deeplinks.sh
- name: Upload to IPFS via web3.storage
uses: dydxprotocol/add-to-web3@v1
-1
View File
@@ -13,7 +13,6 @@ dist
dist-ssr
*.local
*.key
.env
# Editor directories and files
.vscode/*
+31 -44
View File
@@ -5,32 +5,34 @@ import styled from 'styled-components';
import { store } from '@/state/_store';
import { GlobalStyle } from '@/styles/globalStyle';
import { SelectMenu, SelectItem } from '@/components/SelectMenu';
import { AppThemeAndColorModeProvider } from '@/hooks/useAppThemeAndColorMode';
import {
AppTheme,
AppThemeSystemSetting,
AppColorMode,
setAppThemeSetting,
setAppColorMode,
} from '@/state/configs';
import { setLocaleLoaded } from '@/state/localization';
import '@/index.css';
import './ladle.css';
export const StoryWrapper: React.FC<{ children: React.ReactNode }> = ({ children }) => {
const [theme, setTheme] = useState(AppTheme.Classic);
const [colorMode, setColorMode] = useState(AppColorMode.GreenUp);
const [theme, setTheme] = useState('Default theme');
useEffect(() => {
store.dispatch(setAppThemeSetting(theme));
store.dispatch(setAppColorMode(colorMode));
}, [theme, colorMode]);
switch (theme) {
case 'Dark theme': {
document?.documentElement?.classList.remove('theme-light');
document?.documentElement?.classList.add('theme-dark');
break;
}
case 'Light theme': {
document?.documentElement?.classList.remove('theme-dark');
document?.documentElement?.classList.add('theme-light');
break;
}
default: {
document?.documentElement?.classList.remove('theme-dark', 'theme-light');
break;
}
}
}, [theme]);
useEffect(() => {
store.dispatch(setLocaleLoaded(true));
@@ -40,49 +42,34 @@ export const StoryWrapper: React.FC<{ children: React.ReactNode }> = ({ children
<Provider store={store}>
<StoryHeader>
<h4>Active Theme:</h4>
<SelectMenu value={theme} onValueChange={setTheme}>
<SelectMenu
value={theme}
onValueChange={setTheme}
>
{[
{
value: AppTheme.Classic,
value: 'Default theme',
label: 'Default theme',
},
{
value: AppThemeSystemSetting.System,
label: 'System theme',
},
{
value: AppTheme.Dark,
value: 'Dark theme',
label: 'Dark theme',
},
{
value: AppTheme.Light,
value: 'Light theme',
label: 'Light theme',
},
].map(({ value, label }) => (
<SelectItem key={value} value={value} label={label} />
))}
</SelectMenu>
<h4>Active Color Mode:</h4>
<SelectMenu value={colorMode} onValueChange={setColorMode}>
{[
{
value: AppColorMode.GreenUp,
label: 'Green up',
},
{
value: AppColorMode.RedUp,
label: 'Red up',
},
].map(({ value, label }) => (
<SelectItem key={value} value={value} label={label} />
<SelectItem
key={value}
value={value}
label={label}
/>
))}
</SelectMenu>
</StoryHeader>
<hr />
<AppThemeAndColorModeProvider>
<GlobalStyle />
<StoryContent>{children}</StoryContent>
</AppThemeAndColorModeProvider>
<StoryContent>{children}</StoryContent>
</Provider>
);
};
+4 -4
View File
@@ -1,4 +1,4 @@
# Contributing to V4-web
# Contributing to Trader-Fe
We love your input! We want to make contributing to this project as easy and transparent as possible, whether it's:
- Reporting a bug
@@ -18,10 +18,10 @@ Pull requests are the best way to propose changes to the codebase (we use [Githu
4. Issue that pull request!
## Any contributions you make will be under the same License
When you submit code changes, your submissions are understood to be under the same [License](https://github.com/dydxprotocol/v4-web/blob/master/LICENSE) that covers the project.
When you submit code changes, your submissions are understood to be under the same [License](https://github.com/dydxprotocol/trader-fe/blob/master/LICENSE) that covers the project.
## Report bugs using Github's [issues](https://github.com/dydxprotocol/v4-web/issues)
Report a bug by [opening a new issue](https://github.com/dydxprotocol/v4-web/issues/new).
## Report bugs using Github's [issues](https://github.com/dydxprotocol/trader-fe/issues)
Report a bug by [opening a new issue](https://github.com/dydxprotocol/trader-fe/issues/new).
**Great Bug Reports** tend to have:
+39 -3
View File
@@ -1,9 +1,45 @@
Business Source License 1.1
License text copyright © 2023 MariaDB plc, All Rights Reserved. “Business Source License” is a trademark of MariaDB plc.
Copyright (C) 2023 dYdX Trading Inc.
Subject to your compliance with applicable law and the v4 Terms of Use, available at dydx.exchange/legal, you are granted the right to use the Program or Licensed Work (defined below) under the terms of the GNU Affero General Public License as set forth below; provided, however, that if you violate any such applicable law in your use of the Program or Licensed Work, all of your rights and licenses to use (including any rights to reproduce, distribute, install or modify) the Program or Licensed Work will automatically and immediately terminate.
Use of this software before the Change Date specified in the LICENSE file is governed by the Business Source License included in the LICENSE file and at https://spdx.org/licenses/BUSL-1.1.html.
On and after the Change Date specified in the LICENSE file, use of this software will be governed by the Change License (GNU Affero GPL v3) as specified in the LICENSE file.
Parameters
Licensor: dYdX Trading Inc.
Licensed Work: dydxprotocol/cosmos-sdk, dydxprotocol/cometbft, dydxprotocol/v4-chain, dydxprotocol/v4-clients, dydxprotocol/v4-web, dydxprotocol/v4-abacus, dydxprotocol/v4-localization, dydxprotocol/v4-documentation, and any dYdX or dYdX Trading Inc. github repository reflecting a copy of this license, or link to this license.
Additional Use Grant: None
Change Date: On the later to occur of (i) the release of the Licensed Work labeled “dYdX Version 1.0 GPL”, and which includes a document uploaded to the relevant github repository stating “The software in this repository is Version 1.0, subject to GPL license”, and (ii) September 30, 2023 (approximate projected release of “Version 1.0” of this software).
Change License: GNU Affero GPL License v3
License text copyright © 2023 MariaDB plc, All Rights Reserved. “Business Source License” is a trademark of MariaDB plc.
Terms
The Licensor hereby grants you the right to copy, modify, create derivative works, redistribute, and make non-production use of the Licensed Work. The Licensor may make an Additional Use Grant, above, permitting limited production use.
Effective on the Change Date, or the fourth anniversary of the first publicly available distribution of a specific version of the Licensed Work under this License, whichever comes first, the Licensor hereby grants you rights under the terms of the Change License, and the rights granted in the paragraph above terminate.
If your use of the Licensed Work does not comply with the requirements currently in effect as described in this License, you must purchase a commercial license from the Licensor, its affiliated entities, or authorized resellers, or you must refrain from using the Licensed Work.
All copies of the original and modified Licensed Work, and derivative works of the Licensed Work, are subject to this License. This License applies separately for each version of the Licensed Work and the Change Date may vary for each version of the Licensed Work released by Licensor.
You must conspicuously display this License on each original or modified copy of the Licensed Work. If you receive the Licensed Work in original or modified form from a third party, the terms and conditions set forth in this License apply to your use of that work.
Any use of the Licensed Work in violation of this License will automatically terminate your rights under this License for the current and all other versions of the Licensed Work.
This License does not grant you any right in any trademark or logo of Licensor or its affiliates (provided that you may use a trademark or logo of Licensor as expressly required by this License).TO THE EXTENT PERMITTED BY APPLICABLE LAW, THE LICENSED WORK IS PROVIDED ON AN “AS IS” BASIS. LICENSOR HEREBY DISCLAIMS ALL WARRANTIES AND CONDITIONS, EXPRESS OR IMPLIED, INCLUDING (WITHOUT LIMITATION) WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, AND TITLE. MariaDB hereby grants you permission to use this Licenses text to license your works, and to refer to it using the trademark “Business Source License”, as long as you comply with the Covenants of Licensor below.
Covenants of Licensor
In consideration of the right to use this Licenses text and the “Business Source License” name and trademark, Licensor covenants to MariaDB, and to all other recipients of the licensed work to be provided by Licensor:
To specify as the Change License the GPL Version 2.0 or any later version, or a license that is compatible with GPL Version 2.0 or a later version, where “compatible” means that software provided under the Change License can be included in a program with software provided under GPL Version 2.0 or a later version. Licensor may specify additional Change Licenses without limitation.
To either: (a) specify an additional grant of rights to use that does not impose any additional restriction on the right granted in this License, as the Additional Use Grant; or (b) insert the text “None” to specify a Change Date. Not to modify this License in any other way.
Notice
The Business Source License (this document, or the “License”) is not an Open Source license. However, the Licensed Work will eventually be made available under an Open Source License, as stated in this License.
The “Program” or “Licensed Work” shall mean any of the following: dydxprotocol/cosmos-sdk, dydxprotocol/cometbft, dydxprotocol/v4-chain, dydxprotocol/v4-clients, dydxprotocol/v4-web, dydxprotocol/v4-abacus, dydxprotocol/v4-localization, dydxprotocol/v4-documentation, and any dYdX or dYdX Trading Inc. repository reflecting a copy of, or link to, this license.
OPEN SOURCE LICENSE BELOW - TO BE APPLIED AFTER THE CHANGE DATE.
Copyright (C) 2023 dYdX Trading Inc.
Use of this software is governed by the The GNU Affero General Public License as set forth below.
The GNU Affero General Public License
@@ -799,4 +835,4 @@ copy of the Program in return for a fee.
For more information about this software, see https://dydx.exchange.
Copyright (C) 2023 dYdX Trading Inc.
Copyright (C) 2023 dYdX Trading Inc.
+7 -60
View File
@@ -1,17 +1,8 @@
<p align="center"><img src="https://dydx.exchange/icon.svg?" width="256" /></p>
<h1 align="center">dYdX Chain Web</h1>
<div align="center">
<a href='https://github.com/dydxprotocol/v4-web/blob/main/LICENSE'>
<img src='https://img.shields.io/badge/License-AGPL_v3-blue.svg' alt='License' />
</a>
</div>
# v4 Web App
## Prerequisites
- Node.js version 18 and `pnpm` installed on your system
- Wallet Connect account
For deploying with Vercel, create an account with [Vercel](https://vercel.com/signup) if you don't have one already.
@@ -26,16 +17,16 @@ Alternatively, follow the [IPFS Kubo installation guide](https://docs.ipfs.tech
## Part 1: Setting up your local environment
### 1. Clone the repo
### Step 1: Clone the repo
Clone the repository and navigate to its directory:
```bash
git clone https://github.com/dydxprotocol/v4-web.git
cd v4-web
git clone https://github.com/dydxprotocol/trader-fe.git
cd trader-fe
```
### 2. Install pnpm and dependencies
### Step 2: Install pnpm and dependencies
Install pnpm and the project dependencies:
@@ -62,31 +53,7 @@ pnpm ladle
This will automatically open your default browser at `http://localhost:61000`.
## Part 3: Configuring environment
Add or modify the relevant endpoints, links and options in `/public/configs/env.json`.
You'll need to provide a Wallet Connect project id to enable onboarding and wallet connection:
- Create a project on https://cloud.walletconnect.com/app
- Copy over the project ID into this [field](https://github.com/dydxprotocol/v4-web/blob/67ecbd75b43e0c264b7b4d2d9b3d969830b0621c/public/configs/env.json#L822C33-L822C46)
## Part 4: Set Enviornment variables
Set environment variables via `.env`.
- `VITE_BASE_URL` (required): The base URL of the deployment (e.g., `https://www.example.com`).
- `VITE_ALCHEMY_API_KEY` (optional): Add an Alchemy API key for EVM interactions; the app will fall back to public RPCs if not provided.
- `VITE_PK_ENCRYPTION_KEY` (optional): AES encryption key used for signature obfuscation; necessary for enabling the "Remember Me" feature.
- `VITE_V3_TOKEN_ADDRESS` (optional): Address of the V3 $DYDX token.
- `VITE_TOKEN_MIGRATION_URI` (optional): The URL of the token migration website.
- `AMPLITUDE_API_KEY` (optional): Amplitude API key for enabling Amplitude; used with `pnpm run build:inject-amplitude`.
- `BUGSNAG_API_KEY` (optional): API key for enabling Bugsnag; used with `pnpm run build:inject-bugsnag`.
- `IOS_APP_ID` (optional): iOS app ID used for enabling deep linking to the iOS app; used with `pnpm run build:inject-app-deeplinks`.
- `INTERCOM_APP_ID` (optional): Used for enabling Intercom; utilized with `pnpm run build:inject-intercom`.
- `STATUS_PAGE_SCRIPT_URI` (optional): Used for enabling the status page; used with `pnpm run build:inject-statuspage`.
# Deployments
## Deploying with Vercel
## Part 3: Deploying with Vercel
### Step 1: Connect your repository to Vercel
@@ -98,17 +65,12 @@ For the "Build & Development Settings", we recommend the following:
- Framework Preset: `Vite`
- Build Command (override): `pnpm run build`
By default, the dev server runs in development mode and the build command runs in production mode. To override the default mode, you can pass in the `--mode` option flag. For example, if you want to build your app for testnet:
```
pnpm run build --mode testnet
```
If you wish to incorporate analytics via Amplitude and Bugsnag, you can use our scripts:
`pnpm run build:inject-amplitude` and `pnpm run build:inject-bugsnag`. You will need to provide your own API keys for these services. In the Environment Variables section, name the variables as `AMPLITUDE_API_KEY` and `BUGSNAG_API_KEY` and provide the respective keys as their values.
For more details, check out Vercel's [official documentation](https://vercel.com/docs).
## Deploying to IPFS
## Part 4: Deploying to IPFS
### web3.storage: deploy to IPFS via web3.storage using the provided script
@@ -140,18 +102,3 @@ To access your content on IPFS:
2. **Public IPFS gateway:** Access your content via a public IPFS gateway, such as [https://dweb.link](https://dweb.link/) or [https://w3s.link/](https://w3s.link/). Use the gateway URL with your CID appended, like `https://dweb.link/ipfs/your_cid`.
Replace `your_cid` with the actual CID.
## Cloudflare Settings
We recommend that you add your website to Cloudflare for additional security settings.
To block OFAC Sanctioned countries:
1. Navigate Websites > Domain > Security > WAF
2. Create Rule with the following settings:
* If incoming requests match
`(ip.geoip.country eq "CU") or (ip.geoip.country eq "IR") or (ip.geoip.country eq "KP") or (ip.geoip.country eq "SY") or (ip.geoip.country eq "MM") or (ip.geoip.subdivision_1_iso_code eq "UA-09") or (ip.geoip.subdivision_1_iso_code eq "UA-14") or (ip.geoip.subdivision_1_iso_code eq "UA-43")`
* This rule will bring up a Cloudflare page when a restricted geography tries to access your site. You will have the option to display:
1. Custom Text
- (e.g. `Because you appear to be a resident of, or trading from, a jurisdiction that violates our terms of use, or have engaged in activity that violates our terms of use, you have been blocked. You may withdraw your funds from the protocol at any time.`)
2. Default Cloudflare WAF block page
+14 -26
View File
@@ -1,19 +1,17 @@
{
"name": "dydx-chain-web",
"private": true,
"version": "1.0.0",
"version": "0.0.1",
"type": "module",
"license": "AGPL-3.0",
"license": "BSL-1.1",
"engines": {
"node": ">=18"
},
"scripts": {
"dev": "vite",
"build": "vite build",
"build:inject-app-deeplinks": "sh scripts/inject-app-deeplinks.sh",
"build:inject-amplitude": "node scripts/inject-amplitude.js",
"build:inject-bugsnag": "node scripts/inject-bugsnag.js",
"build:inject-intercom": "node scripts/inject-intercom.js",
"build:inject-statuspage": "node scripts/inject-statuspage.js",
"deploy:ipfs": "node scripts/upload-ipfs.js --verbose",
"deploy:update-ipns": "node scripts/update-ipns.js",
@@ -32,24 +30,21 @@
},
"packageManager": "pnpm@8.6.6",
"dependencies": {
"@0xsquid/sdk": "^1.10.0",
"@cosmjs/amino": "^0.32.1",
"@cosmjs/crypto": "^0.32.1",
"@cosmjs/encoding": "^0.32.1",
"@cosmjs/proto-signing": "^0.32.1",
"@cosmjs/stargate": "^0.32.1",
"@cosmjs/tendermint-rpc": "^0.32.1",
"@dydxprotocol/v4-abacus": "^1.4.2",
"@dydxprotocol/v4-client-js": "^1.0.20",
"@dydxprotocol/v4-localization": "^1.1.24",
"@0xsquid/sdk": "^1.7.2",
"@cosmjs/amino": "^0.31.0",
"@cosmjs/crypto": "^0.31.0",
"@cosmjs/encoding": "^0.31.0",
"@cosmjs/proto-signing": "^0.31.0",
"@cosmjs/stargate": "^0.31.0",
"@cosmjs/tendermint-rpc": "^0.31.0",
"@dydxprotocol/abacus": "^0.4.1",
"@dydxprotocol/v4-client-js": "^0.32.0",
"@dydxprotocol/v4-localization": "^0.0.21",
"@ethersproject/providers": "^5.7.2",
"@js-joda/core": "^5.5.3",
"@radix-ui/react-accordion": "^1.1.2",
"@radix-ui/react-checkbox": "^1.0.4",
"@radix-ui/react-collapsible": "^1.0.3",
"@radix-ui/react-dialog": "^1.0.4",
"@radix-ui/react-dropdown-menu": "^2.0.5",
"@radix-ui/react-icons": "^1.3.0",
"@radix-ui/react-navigation-menu": "^1.1.3",
"@radix-ui/react-popover": "^1.0.6",
"@radix-ui/react-radio-group": "^1.1.3",
@@ -92,7 +87,6 @@
"buffer": "^6.0.3",
"cmdk": "^0.2.0",
"color": "^4.2.3",
"cosmjs-types": "^0.9.0",
"crypto-js": "^4.1.1",
"ethers": "^6.6.1",
"graz": "^0.0.43",
@@ -100,7 +94,6 @@
"long": "^5.2.3",
"luxon": "^3.3.0",
"qr-code-styling": "1.6.0-rc.1",
"query-string": "^8.1.0",
"react": "^18.2.0",
"react-aria": "^3.25.0",
"react-dom": "^18.2.0",
@@ -112,8 +105,8 @@
"reselect": "^4.1.8",
"styled-components": "^5.3.11",
"use-latest": "^1.2.1",
"viem": "^1.20.0",
"wagmi": "^1.4.12"
"viem": "^1.1.6",
"wagmi": "^1.3.7"
},
"devDependencies": {
"@babel/core": "^7.22.5",
@@ -156,10 +149,5 @@
"vitest": "^0.32.2",
"w3name": "^1.0.8",
"web3.storage": "^4.5.4"
},
"pnpm": {
"overrides": {
"follow-redirects": "1.15.3"
}
}
}
+2359 -1413
View File
File diff suppressed because it is too large Load Diff
+38 -14
View File
@@ -4,47 +4,71 @@
"details": [
{
"appIDs": [
"{PLACE_YOUR_APP_ID_HERE}"
"75C6UARB5H.exchangeV4.dydx.trading"
],
"components": [
{
"/": "/"
"/": "/trade/*"
},
{
"/": "/#/"
"/": "/r/*"
},
{
"/": "*/markets"
"/": "/portfolio/overview"
},
{
"/": "*/trade"
"/": "/portfolio/positions"
},
{
"/": "*/trade/*"
"/": "/portfolio/orders"
},
{
"/": "*/portfolio"
"/": "/portfolio/fees"
},
{
"/": "*/portfolio/overview"
"/": "/portfolio/history"
},
{
"/": "*/portfolio/positions"
"/": "/portfolio/history/trades"
},
{
"/": "*/portfolio/orders"
"/": "/portfolio/history/transfers"
},
{
"/": "*/portfolio/fees"
"/": "/portfolio/history/funding"
},
{
"/": "*/portfolio/history/trades"
"/": "/rewards"
},
{
"/": "*/portfolio/history/transfers"
"/": "/my-profile"
},
{
"/": "*/portfolio/history/funding"
"/": "/rankings/hedgies"
},
{
"/": "/rankings/competition"
},
{
"/": "/rankings/diamond"
},
{
"/": "/rankings/platinum"
},
{
"/": "/rankings/gold"
},
{
"/": "/rankings/silver"
},
{
"/": "/rankings/bronze"
},
{
"/": "/rankings/pnl-absolute"
},
{
"/": "/rankings/pnl-percent"
},
{
"/": "/walletsegue",
-22
View File
@@ -1,22 +0,0 @@
[
{
"relation": ["delegate_permission/common.handle_all_urls"],
"target": {
"namespace": "android_app",
"package_name": "exchange.dydx.trading.debug",
"sha256_cert_fingerprints": [
"8A:9C:CC:49:B0:35:9A:91:67:CB:98:B0:B5:87:92:5F:9E:B7:EF:CE:A0:47:57:85:A4:35:3E:0C:E1:56:9E:A2"
]
}
},
{
"relation": ["delegate_permission/common.handle_all_urls"],
"target": {
"namespace": "android_app",
"package_name": "exchange.dydx.trading",
"sha256_cert_fingerprints": [
"B2:2D:CC:27:9D:52:05:98:63:C9:7B:34:36:70:A3:8E:00:31:28:08:2D:2E:70:76:C9:31:AE:F9:55:21:15:A5"
]
}
}
]
Binary file not shown.

After

Width:  |  Height:  |  Size: 193 KiB

-19
View File
@@ -1,19 +0,0 @@
<svg width="120" height="97" viewBox="0 0 120 97" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect y="8" width="120" height="1" fill="currentColor"/>
<rect y="18" width="120" height="1" fill="currentColor"/>
<rect y="28" width="120" height="1" fill="currentColor"/>
<rect y="38" width="120" height="1" fill="currentColor"/>
<rect y="48" width="120" height="1" fill="currentColor"/>
<rect y="58" width="120" height="1" fill="currentColor"/>
<rect y="68" width="120" height="1" fill="currentColor"/>
<rect y="78" width="120" height="1" fill="currentColor"/>
<rect y="88" width="120" height="1" fill="currentColor"/>
<rect x="18" width="1" height="97" fill="currentColor"/>
<rect x="32" width="1" height="97" fill="currentColor"/>
<rect x="46" width="1" height="97" fill="currentColor"/>
<rect x="60" width="1" height="97" fill="currentColor"/>
<rect x="74" width="1" height="97" fill="currentColor"/>
<rect x="88" width="1" height="97" fill="currentColor"/>
<rect x="102" width="1" height="97" fill="currentColor"/>
<path d="M0 0H120V97H0V0Z" fill="url(#paint0_radial_314_37586)"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB

-20
View File
@@ -1,20 +0,0 @@
<svg width="90" height="39" viewBox="0 0 90 39" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect y="31.2344" width="2.57142" height="7.71427" fill="#3ED9A4"/>
<rect x="5.14258" y="28.6648" width="2.57142" height="5.14284" fill="#E45555"/>
<rect x="25.7139" y="26.0923" width="2.57142" height="10.2857" fill="#E45555"/>
<rect x="51.4283" y="28.6648" width="2.57142" height="5.14284" fill="#E45555"/>
<rect x="56.5712" y="31.2344" width="2.57142" height="5.14284" fill="#E45555"/>
<rect x="77.1426" y="18.3767" width="2.57142" height="5.14284" fill="#E45555"/>
<rect x="41.1427" y="23.5198" width="2.57142" height="5.14284" fill="#E45555"/>
<rect x="10.2856" y="28.6648" width="2.57142" height="10.2857" fill="#3ED9A4"/>
<rect x="61.7143" y="23.5198" width="2.57142" height="10.2857" fill="#3ED9A4"/>
<rect x="66.8569" y="20.9492" width="2.57142" height="5.14284" fill="#3ED9A4"/>
<rect x="71.9997" y="13.2346" width="2.57142" height="7.71427" fill="#3ED9A4"/>
<rect x="82.2856" y="10.6631" width="2.57142" height="10.2857" fill="#3ED9A4"/>
<rect x="87.4287" y="0.37793" width="2.57142" height="15.4285" fill="#3ED9A4"/>
<rect x="15.4284" y="26.0923" width="2.57142" height="5.14284" fill="#3ED9A4"/>
<rect x="20.5714" y="23.5198" width="2.57142" height="5.14284" fill="#3ED9A4"/>
<rect x="30.857" y="31.2344" width="2.57142" height="5.14284" fill="#3ED9A4"/>
<rect x="35.9999" y="26.0923" width="2.57142" height="5.14284" fill="#3ED9A4"/>
<rect x="46.2853" y="26.0923" width="2.57142" height="2.57142" fill="#3ED9A4"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.5 KiB

File diff suppressed because it is too large Load Diff

Before

Width:  |  Height:  |  Size: 2.2 MiB

File diff suppressed because it is too large Load Diff

Before

Width:  |  Height:  |  Size: 2.1 MiB

-12
View File
@@ -1,12 +0,0 @@
[
{
"chainId": "1",
"tokenAddress": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
"name": "Ethereum"
},
{
"chainId": "5",
"tokenAddress": "0x07865c6E87B9F70255377e024ace6630C1Eaa37F",
"name": "Ethereum Goerli"
}
]
-16
View File
@@ -1,16 +0,0 @@
{
"tradingRewardsFAQs": [
{
"questionLocalizationKey": "APP.TRADING_REWARDS.FAQ_WHO_IS_ELIGIBLE_QUESTION",
"answerLocalizationKey": "APP.TRADING_REWARDS.FAQ_WHO_IS_ELIGIBLE_ANSWER"
},
{
"questionLocalizationKey": "APP.TRADING_REWARDS.FAQ_HOW_DO_TRADING_REWARDS_WORK_QUESTION",
"answerLocalizationKey": "APP.TRADING_REWARDS.FAQ_HOW_DO_TRADING_REWARDS_WORK_ANSWER"
},
{
"questionLocalizationKey": "APP.TRADING_REWARDS.FAQ_HOW_DO_I_CLAIM_MY_REWARDS_QUESTION",
"answerLocalizationKey": "APP.TRADING_REWARDS.FAQ_HOW_DO_I_CLAIM_MY_REWARDS_ANSWER"
}
]
}
File diff suppressed because it is too large Load Diff
-8
View File
@@ -1,8 +0,0 @@
[
{
"name": "coinbase",
"label": "Coinbase",
"icon": "/exchanges/coinbase.png",
"depositType": "noble"
}
]
-367
View File
@@ -1,367 +0,0 @@
{
"1INCH-USD": {
"name": "1inch",
"tags": ["Defi"],
"websiteLink": "https://1inch.io/",
"whitepaperLink": "https://github.com/1inch",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/1inch/"
},
"AAVE-USD": {
"name": "Aave",
"tags": ["Defi"],
"websiteLink": "https://aave.com/",
"whitepaperLink": "https://github.com/aave/protocol-v2/blob/master/aave-v2-whitepaper.pdf",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/aave/"
},
"ADA-USD": {
"name": "Cardano",
"tags": ["Layer 1"],
"websiteLink": "https://cardano.org/",
"whitepaperLink": "https://why.cardano.org/en/introduction/motivation/",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/cardano/"
},
"ALGO-USD": {
"name": "Algorand",
"tags": ["Layer 1"],
"websiteLink": "https://algorand.com/",
"whitepaperLink": "https://algorand.com/technology/white-papers",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/algorand/"
},
"APE-USD": {
"name": "ApeCoin",
"tags": [],
"websiteLink": "https://apecoin.com/",
"whitepaperLink": "https://apecoin.com/about",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/apecoin-ape/"
},
"APT-USD": {
"name": "Aptos",
"tags": ["Layer 1"],
"websiteLink": "https://aptoslabs.com/",
"whitepaperLink": "https://aptos.dev/aptos-white-paper/",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/aptos/"
},
"ARB-USD": {
"name": "Arbitrum",
"tags": [],
"websiteLink": "https://arbitrum.io/",
"whitepaperLink": "https://github.com/OffchainLabs/nitro",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/arbitrum/"
},
"ATOM-USD": {
"name": "Cosmos",
"tags": ["Layer 1"],
"websiteLink": "https://cosmos.network/",
"whitepaperLink": "https://v1.cosmos.network/resources/whitepaper",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/cosmos/"
},
"AVAX-USD": {
"name": "Avalanche",
"tags": ["Layer 1"],
"websiteLink": "https://www.avalabs.org/",
"whitepaperLink": "https://assets.website-files.com/5d80307810123f5ffbb34d6e/6008d7bbf8b10d1eb01e7e16_Avalanche%20Platform%20Whitepaper.pdf",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/avalanche/"
},
"BCH-USD": {
"name": "Bitcoin Cash",
"tags": ["Layer 1"],
"websiteLink": "https://bitcoincash.org/",
"whitepaperLink": "https://bitcoincash.org/",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/bitcoin-cash/"
},
"BONK-USD": {
"name": "BONK COIN",
"tags": [],
"websiteLink": "https://bonkcoin.com/",
"whitepaperLink": "https://bonkcoin.com/",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/bonk1/"
},
"BLUR-USD": {
"name": "Blur",
"tags": [],
"websiteLink": "https://blur.io/",
"whitepaperLink": "https://docs.blur.foundation/",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/blur-token/"
},
"CELO-USD": {
"name": "Celo",
"tags": [],
"websiteLink": "https://celo.org",
"whitepaperLink": "https://docs.celo.org",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/celo/"
},
"BTC-USD": {
"name": "Bitcoin",
"tags": ["Layer 1"],
"websiteLink": "https://bitcoin.org/",
"whitepaperLink": "https://bitcoin.org/bitcoin.pdf",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/bitcoin/"
},
"COMP-USD": {
"name": "Compound",
"tags": ["Defi"],
"websiteLink": "https://compound.finance/",
"whitepaperLink": "https://compound.finance/documents/Compound.Whitepaper.pdf",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/compound/"
},
"CRV-USD": {
"name": "Curve",
"tags": ["Governance"],
"websiteLink": "https://curve.fi/",
"whitepaperLink": "https://curve.fi/whitepaper",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/curve-dao-token/"
},
"DOGE-USD": {
"name": "Dogecoin",
"tags": ["Layer 1"],
"websiteLink": "https://dogecoin.com/",
"whitepaperLink": "https://github.com/dogecoin/dogecoin",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/dogecoin/"
},
"DOT-USD": {
"name": "Polkadot",
"tags": ["Layer 1"],
"websiteLink": "https://polkadot.network/",
"whitepaperLink": "https://polkadot.network/PolkaDotPaper.pdf",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/polkadot-new/"
},
"ENJ-USD": {
"name": "Enjin",
"tags": [],
"websiteLink": "https://enjin.io/",
"whitepaperLink": "https://cdn.enjin.io/downloads/whitepapers/enjin-coin/en.pdf/",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/enjin-coin/"
},
"EOS-USD": {
"name": "EOS",
"tags": ["Layer 1"],
"websiteLink": "https://eos.io/",
"whitepaperLink": "https://github.com/EOSIO/Documentation/blob/master/TechnicalWhitePaper.md",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/eos/"
},
"ETC-USD": {
"name": "Ethereum Classic",
"tags": ["Layer 1"],
"websiteLink": "https://ethereumclassic.org/",
"whitepaperLink": "https://ethereumclassic.org/why-classic",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/ethereum-classic/"
},
"ETH-USD": {
"name": "Ethereum",
"tags": ["Layer 1"],
"websiteLink": "https://ethereum.org/",
"whitepaperLink": "https://ethereum.org/whitepaper/",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/ethereum/",
"displayStepSize": "0.001",
"displayTickSize": "0.1"
},
"FIL-USD": {
"name": "Filecoin",
"tags": ["Layer 1"],
"websiteLink": "https://filecoin.io/",
"whitepaperLink": "https://filecoin.io/filecoin.pdf",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/filecoin/"
},
"HNT-USD": {
"name": "Helium",
"tags": ["Layer 1"],
"websiteLink": "https://www.helium.com",
"whitepaperLink": "http://whitepaper.helium.com",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/helium/"
},
"ICP-USD": {
"name": "Internet Computer",
"tags": ["Layer 1"],
"websiteLink": "https://dfinity.org",
"whitepaperLink": "https://dfinity.org/whitepaper.pdf",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/internet-computer/"
},
"LDO-USD": {
"name": "Lido DAO",
"tags": ["Defi"],
"websiteLink": "https://lido.fi/",
"whitepaperLink": "https://lido.fi/static/Lido:Ethereum-Liquid-Staking.pdf",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/lido-dao/"
},
"LINK-USD": {
"name": "Chainlink",
"tags": ["Defi"],
"websiteLink": "https://chain.link/",
"whitepaperLink": "https://link.smartcontract.com/whitepaper",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/chainlink/"
},
"LTC-USD": {
"name": "Litecoin",
"tags": ["Layer 1"],
"websiteLink": "https://litecoin.org/",
"whitepaperLink": "https://litecoin.info/index.php/Main_Page",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/litecoin/"
},
"MATIC-USD": {
"name": "Polygon",
"tags": ["Layer 2"],
"websiteLink": "https://polygon.technology/",
"whitepaperLink": "https://polygon.technology/lightpaper-polygon.pdf",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/polygon/"
},
"MKR-USD": {
"name": "Maker",
"tags": ["Governance"],
"websiteLink": "https://makerdao.com",
"whitepaperLink": "https://makerdao.com/whitepaper",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/maker/"
},
"NEAR-USD": {
"name": "NEAR Protocol",
"tags": ["Layer 1"],
"websiteLink": "https://near.org",
"whitepaperLink": "https://near.org/papers/the-official-near-white-paper/",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/near-protocol/"
},
"OP-USD": {
"name": "Optimism",
"tags": [],
"websiteLink": "https://www.optimism.io/",
"whitepaperLink": "https://github.com/ethereum-optimism",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/optimism-ethereum/"
},
"PEPE-USD": {
"name": "Pepe",
"tags": [],
"websiteLink": "https://www.pepe.vip/",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/pepe/"
},
"RUNE-USD": {
"name": "THORChain",
"tags": ["Layer 1"],
"websiteLink": "https://thorchain.org",
"whitepaperLink": "https://whitepaper.io/document/709/thorchain-whitepaper",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/thorchain/"
},
"SEI-USD": {
"name": "Sei",
"tags": ["Layer 1", "Defi"],
"websiteLink": "https://www.sei.io/",
"whitepaperLink": "https://github.com/sei-protocol/sei-chain/blob/3c9576fee3494ce039df684624f918dd8066ba3f/whitepaper/Sei_Whitepaper.pdf",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/sei/"
},
"SHIB-USD": {
"name": "Shiba Inu",
"tags": [],
"websiteLink": "https://shibatoken.com/",
"whitepaperLink": "https://docs.shibatoken.com/",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/shiba-inu/"
},
"SNX-USD": {
"name": "Synthetix",
"tags": ["Defi"],
"websiteLink": "https://synthetix.io/",
"whitepaperLink": "https://docs.synthetix.io/litepaper",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/synthetix-network-token/"
},
"SOL-USD": {
"name": "Solana",
"tags": ["Layer 1"],
"websiteLink": "https://solana.com/",
"whitepaperLink": "https://solana.com/solana-whitepaper.pdf",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/solana/"
},
"SUI-USD": {
"name": "Sui",
"tags": ["Layer 1"],
"websiteLink": "https://sui.io/",
"whitepaperLink": "https://github.com/MystenLabs",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/sui/"
},
"SUSHI-USD": {
"name": "Sushi",
"tags": ["Defi"],
"websiteLink": "https://sushi.com/",
"whitepaperLink": "https://docs.sushi.com/",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/sushiswap/"
},
"TIA-USD": {
"name": "Celestia",
"tags": ["Layer 1"],
"websiteLink": "https://celestia.org/",
"whitepaperLink": "https://arxiv.org/pdf/1905.09274.pdf",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/celestia/"
},
"TRX-USD": {
"name": "TRON",
"tags": ["Defi"],
"websiteLink": "https://tron.network/",
"whitepaperLink": "https://tron.network/static/doc/white_paper_v_2_0.pdf",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/tron/"
},
"UMA-USD": {
"name": "UMA",
"tags": ["Defi"],
"websiteLink": "https://umaproject.org/",
"whitepaperLink": "https://github.com/UMAprotocol/whitepaper",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/uma/"
},
"UNI-USD": {
"name": "Uniswap",
"tags": ["Defi"],
"websiteLink": "https://uniswap.org/",
"whitepaperLink": "https://uniswap.org/whitepaper-v3.pdf",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/uniswap/"
},
"WLD-USD": {
"name": "Worldcoin",
"tags": [],
"websiteLink": "https://worldcoin.org/",
"whitepaperLink": "https://whitepaper.worldcoin.org/",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/worldcoin-org/"
},
"XLM-USD": {
"name": "Stellar",
"tags": ["Layer 1"],
"websiteLink": "https://www.stellar.org/",
"whitepaperLink": "https://www.stellar.org/papers/stellar-consensus-protocol",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/stellar/"
},
"XMR-USD": {
"name": "Monero",
"tags": ["Layer 1"],
"websiteLink": "https://www.getmonero.org/",
"whitepaperLink": "https://www.getmonero.org/resources/research-lab/",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/monero/"
},
"XRP-USD": {
"name": "Ripple",
"tags": ["Layer 1"],
"websiteLink": "https://ripple.com/currency/",
"whitepaperLink": "https://github.com/ripple",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/xrp/"
},
"XTZ-USD": {
"name": "Tezos",
"tags": ["Layer 1"],
"websiteLink": "https://tezos.com",
"whitepaperLink": "https://tezos.com/whitepaper.pdf",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/tezos/"
},
"YFI-USD": {
"name": "Yearn",
"tags": ["Defi"],
"websiteLink": "https://yearn.finance/",
"whitepaperLink": "https://docs.yearn.finance/",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/yearn-finance/"
},
"ZEC-USD": {
"name": "Zcash",
"tags": ["Layer 1"],
"websiteLink": "https://z.cash/",
"whitepaperLink": "https://z.cash/technology/",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/zcash/"
},
"ZRX-USD": {
"name": "0x",
"tags": ["Defi"],
"websiteLink": "https://0x.org/",
"whitepaperLink": "https://0x.org/pdfs/0x_white_paper.pdf",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/0x/"
}
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
-472
View File
@@ -1,472 +0,0 @@
[
{
"id": "c57ca95b47569778a828d19178114f4db188b89b763c899ba0be274e97267d96",
"name": "MetaMask",
"description": "",
"homepage": "https://metamask.io/",
"chains": [
"eip155:1",
"eip155:42161",
"eip155:43114"
],
"versions": [
"1"
],
"app": {
"browser": "",
"ios": "https://apps.apple.com/us/app/metamask/id1438144202",
"android": "https://play.google.com/store/apps/details?id=io.metamask",
"mac": "",
"windows": "",
"linux": ""
},
"mobile": {
"native": "metamask:",
"universal": "https://metamask.app.link"
},
"desktop": {
"native": "",
"universal": ""
},
"metadata": {
"shortName": "MetaMask",
"colors": {
"primary": "rgb(255, 255, 255)",
"secondary": ""
}
},
"config": {
"comment": "MetaMask",
"iosMinVersion": "0",
"imageUrl": "",
"encoding": "=\"#%/<>?@\\^`{|}:&",
"androidPackage": "io.metamask",
"connections": [
{
"type": "walletConnectV2",
"native": "metamask:",
"universal": "https://metamask.app.link"
}
],
"methods": [
"eth_sendTransaction",
"personal_sign",
"eth_signTypedData",
"wallet_addEthereumChain"
]
},
"userFields": {
"analyticEvent": "METAMASK",
"imageName": "metamask.png"
}
},
{
"id": "coinbase-wallet",
"name": "Coinbase Wallet",
"description": "",
"homepage": "https://www.coinbase.com/wallet",
"chains": [
"eip155:1"
],
"versions": [
"1"
],
"app": {
"browser": "",
"ios": "https://apps.apple.com/us/app/coinbase-wallet-nfts-crypto/id1278383455",
"android": "https://play.google.com/store/apps/details?id=org.toshi",
"mac": "",
"windows": "",
"linux": ""
},
"mobile": {
"native": "cbwallet:",
"universal": ""
},
"desktop": {
"native": "",
"universal": ""
},
"metadata": {
"shortName": "Coinbase",
"colors": {
"primary": "rgb(255, 255, 255)",
"secondary": ""
}
},
"config": {
"comment": "Coinbase",
"iosMinVersion": "1.8.0",
"imageUrl": "",
"encoding": "=\"#%/<>?@\\^`{|}:&",
"androidPackage": "org.toshi",
"connections": [
{
"type": "walletSegue",
"native": "cbwallet:",
"universal": ""
}
],
"methods": [
"eth_sendTransaction",
"personal_sign",
"eth_signTypedData",
"wallet_addEthereumChain"
]
},
"userFields": {
"analyticEvent": "COINBASE_WALLET",
"imageName": "coinbase-wallet.png"
}
},
{
"id": "9d373b43ad4d2cf190fb1a774ec964a1addf406d6fd24af94ab7596e58c291b2",
"name": "imToken",
"description": "",
"homepage": "https://token.im/",
"chains": [
"eip155:1"
],
"versions": [
"1"
],
"app": {
"browser": "",
"ios": "https://itunes.apple.com/us/app/imtoken2/id1384798940",
"android": "https://play.google.com/store/apps/details?id=im.token.app",
"mac": "",
"windows": "",
"linux": ""
},
"mobile": {
"native": "imtokenv2:",
"universal": ""
},
"desktop": {
"native": "",
"universal": ""
},
"metadata": {
"shortName": "imToken",
"colors": {
"primary": "rgb(255, 255, 255)",
"secondary": ""
}
},
"config": {
"comment": "imToken",
"iosMinVersion": "0",
"imageUrl": "",
"androidPackage": "im.token.app",
"connections": [
{
"type": "walletConnectV2",
"native": "imtokenv2:",
"universal": ""
}
],
"methods": [
"eth_sendTransaction",
"personal_sign",
"eth_signTypedData"
]
},
"userFields": {
"analyticEvent": "IMTOKEN",
"imageName": "imtoken.png"
}
},
{
"id": "4622a2b2d6af1c9844944291e5e7351a6aa24cd7b23099efac1b2fd875da31a0",
"name": "Trust Wallet",
"description": "",
"homepage": "https://trustwallet.com/",
"chains": [
"eip155:1",
"eip155:42161",
"eip155:43114"
],
"versions": [
"1"
],
"app": {
"browser": "",
"ios": "https://apps.apple.com/app/apple-store/id1288339409",
"android": "https://play.google.com/store/apps/details?id=com.wallet.crypto.trustapp",
"mac": "",
"windows": "",
"linux": ""
},
"mobile": {
"native": "trust:",
"universal": ""
},
"desktop": {
"native": "",
"universal": ""
},
"metadata": {
"shortName": "Trust",
"colors": {
"primary": "rgb(51, 117, 187)",
"secondary": ""
}
},
"config": {
"comment": "Trust Wallet",
"iosMinVersion": "0",
"encoding": "=\"#%/<>?@\\^`{|}:&",
"imageUrl": "",
"androidPackage": "com.wallet.crypto.trustapp",
"connections": [
{
"type": "walletConnectV2",
"native": "trust:",
"universal": "https://link.trustwallet.com"
}
],
"methods": [
"eth_sendTransaction",
"personal_sign",
"eth_signTypedData"
]
},
"userFields": {
"analyticEvent": "TRUST_WALLET",
"imageName": "trust-wallet.png"
}
},
{
"id": "Zerion",
"name": "Zerion",
"description": "",
"homepage": "https://zerion.io/",
"chains": [
"eip155:1"
],
"versions": [
"1"
],
"app": {
"browser": "",
"ios": "https://apps.apple.com/app/apple-store/id1456732565",
"android": "https://play.google.com/store/apps/details?id=io.zerion.android",
"mac": "",
"windows": "",
"linux": ""
},
"mobile": {
"native": "zerion:",
"universal": ""
},
"desktop": {
"native": "",
"universal": ""
},
"metadata": {
"shortName": "Zerion",
"colors": {
"primary": "rgb(51, 117, 187)",
"secondary": ""
}
},
"config": {
"comment": "Zerion Wallet",
"iosMinVersion": "0",
"encoding": "=\"#%/<>?@\\^`{|}:&",
"imageUrl": "",
"androidPackage": "io.zerion.android",
"connections": [
{
"type": "walletConnectV2",
"native": "zerion:",
"universal": ""
}
],
"methods": [
"eth_sendTransaction",
"personal_sign",
"eth_signTypedData"
]
},
"userFields": {
"analyticEvent": "ZERION",
"imageName": "zerion.png"
}
},
{
"id": "20459438007b75f4f4acb98bf29aa3b800550309646d375da5fd4aac6c2a2c66",
"name": "TokenPocket",
"description": "",
"homepage": "https://tokenpocket.pro/",
"chains": [
"eip155:1"
],
"versions": [
"1"
],
"app": {
"browser": "",
"ios": "https://apps.apple.com/us/app/tokenpocket-trusted-wallet/id1436028697",
"android": "https://play.google.com/store/apps/details?id=vip.mytokenpocket",
"mac": "",
"windows": "",
"linux": ""
},
"mobile": {
"native": "tpoutside:",
"universal": ""
},
"desktop": {
"native": "",
"universal": ""
},
"metadata": {
"shortName": "TokenPocket",
"colors": {
"primary": "rgb(41, 128, 254)",
"secondary": ""
}
},
"config": {
"comment": "TokenPocket",
"iosMinVersion": "0",
"imageUrl": "",
"androidPackage": "vip.mytokenpocket",
"connections": [
{
"type": "walletConnectV2",
"native": "tpoutside:",
"universal": ""
}
],
"methods": [
"eth_sendTransaction",
"personal_sign",
"eth_signTypedData"
]
},
"userFields": {
"analyticEvent": "TOKEN_POCKET",
"imageName": "tokenpocket.png"
}
},
{
"id": "1ae92b26df02f0abca6304df07debccd18262fdf5fe82daa81593582dac9a369",
"name": "Rainbow",
"description": "",
"homepage": "https://rainbow.me/",
"chains": [
"eip155:1"
],
"versions": [
"1"
],
"app": {
"browser": "",
"ios": "https://apps.apple.com/us/app/rainbow-ethereum-wallet/id1457119021",
"android": "",
"mac": "",
"windows": "",
"linux": ""
},
"mobile": {
"native": "rainbow:",
"universal": "https://rnbwapp.com"
},
"desktop": {
"native": "",
"universal": ""
},
"metadata": {
"shortName": "Rainbow",
"colors": {
"primary": "rgb(0, 30, 89)",
"secondary": ""
}
},
"config": {
"comment": "Rainbow",
"iosMinVersion": "0",
"encoding": "=\"#%/<>?@\\^`{|}:&",
"androidPackage": "me.rainbow",
"backlinked": true,
"imageUrl": "",
"connections": [
{
"type": "walletConnectV2",
"native": "rainbow:",
"universal": "https://rnbwapp.com"
}
],
"methods": [
"eth_sendTransaction",
"personal_sign",
"eth_signTypedData"
]
},
"userFields": {
"analyticEvent": "RAINBOW_WALLET",
"imageName": "rainbow-wallet.png"
}
},
{
"id": "09102e7bbbd3f92001eda104abe23803181629f695e8f1b95af96d88ff7d5890",
"name": "1inch Wallet",
"description": "",
"homepage": "https://1inch.io/wallet/",
"chains": [
"eip155:1",
"eip155:56"
],
"versions": [
"1"
],
"app": {
"browser": "",
"ios": "https://apps.apple.com/us/app/1inch-defi-wallet/id1546049391",
"android": "",
"mac": "",
"windows": "",
"linux": ""
},
"mobile": {
"native": "oneinch:",
"universal": "https://wallet.1inch.io"
},
"desktop": {
"native": "",
"universal": ""
},
"metadata": {
"shortName": "1inch",
"colors": {
"primary": "rgb(31, 36, 46)",
"secondary": ""
}
},
"config": {
"comment": "1inch",
"iosMinVersion": "0",
"encoding": "=\"#%/<>?@\\^`{|}:&",
"androidPackage": "io.oneinch.android",
"imageUrl": "",
"connections": [
{
"type": "walletConnectV2",
"native": "oneinch:",
"universal": "https://wallet.1inch.io"
}
],
"methods": [
"eth_sendTransaction",
"personal_sign",
"eth_signTypedData"
]
},
"userFields": {
"analyticEvent": "1INCH",
"imageName": "1inch.png"
}
}
]
Binary file not shown.

Before

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

-230
View File
@@ -1,230 +0,0 @@
<svg width="254" height="77" viewBox="0 0 254 77" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="Group 1258">
<g id="Ellipse 9" opacity="0.4" filter="url(#filter0_f_20300_14972)">
<ellipse cx="86.5425" cy="-3.91219" rx="116.542" ry="20.0878" fill="#7774FF"/>
</g>
<g id="Ellipse 10" opacity="0.3" filter="url(#filter1_f_20300_14972)">
<circle cx="74.5734" cy="4.63007" r="1.00507" fill="#9A9AFF"/>
</g>
<g id="Ellipse 12" opacity="0.3" filter="url(#filter2_f_20300_14972)">
<circle cx="82.1105" cy="7.64179" r="1.00507" fill="#9A9AFF"/>
</g>
<g id="Ellipse 22" opacity="0.3" filter="url(#filter3_f_20300_14972)">
<circle cx="116.785" cy="4.63007" r="1.00507" fill="#9A9AFF"/>
</g>
<g id="Ellipse 11" opacity="0.3" filter="url(#filter4_f_20300_14972)">
<circle cx="64.774" cy="8.90488" r="0.502536" fill="#9A9AFF"/>
</g>
<g id="Ellipse 18" opacity="0.3" filter="url(#filter5_f_20300_14972)">
<circle cx="96.4322" cy="3.87363" r="0.502536" fill="#9A9AFF"/>
</g>
<g id="Ellipse 20" opacity="0.3" filter="url(#filter6_f_20300_14972)">
<circle cx="76.0807" cy="14.9361" r="0.502536" fill="#9A9AFF"/>
</g>
<g id="Ellipse 21" opacity="0.3" filter="url(#filter7_f_20300_14972)">
<circle cx="104.725" cy="4.87754" r="0.502536" fill="#9A9AFF"/>
</g>
<g id="Ellipse 23" opacity="0.3" filter="url(#filter8_f_20300_14972)">
<circle cx="114.021" cy="7.13925" r="0.502536" fill="#9A9AFF"/>
</g>
<g id="Ellipse 24" opacity="0.3" filter="url(#filter9_f_20300_14972)">
<circle cx="109.75" cy="15.4361" r="0.502536" fill="#9A9AFF"/>
</g>
<g id="Ellipse 25" opacity="0.3" filter="url(#filter10_f_20300_14972)">
<circle cx="119.801" cy="12.6705" r="0.502536" fill="#9A9AFF"/>
</g>
<g id="Ellipse 27" opacity="0.3" filter="url(#filter11_f_20300_14972)">
<circle cx="94.0885" cy="11.0182" r="0.502536" fill="#9A9AFF"/>
</g>
<g id="Ellipse 30" opacity="0.3" filter="url(#filter12_f_20300_14972)">
<circle cx="154.475" cy="11.6666" r="0.502536" fill="#9A9AFF"/>
</g>
<g id="Ellipse 29" opacity="0.3" filter="url(#filter13_f_20300_14972)">
<circle cx="146.435" cy="8.90488" r="0.502536" fill="#9A9AFF"/>
</g>
<g id="Ellipse 15" opacity="0.3" filter="url(#filter14_f_20300_14972)">
<circle cx="24.0103" cy="4.44785" r="0.502536" fill="#9A9AFF"/>
</g>
<g id="Ellipse 33" opacity="0.3" filter="url(#filter15_f_20300_14972)">
<circle cx="-1.05899" cy="13.6783" r="0.502536" fill="#9A9AFF"/>
</g>
<g id="Ellipse 16" opacity="0.3" filter="url(#filter16_f_20300_14972)">
<circle cx="17.441" cy="16.4908" r="0.502536" fill="#9A9AFF"/>
</g>
<g id="Ellipse 35" opacity="0.3" filter="url(#filter17_f_20300_14972)">
<circle cx="36.0553" cy="6.63535" r="0.502536" fill="#9A9AFF"/>
</g>
<g id="Ellipse 32" opacity="0.3" filter="url(#filter18_f_20300_14972)">
<circle cx="10.8707" cy="14.2994" r="0.502536" fill="#9A9AFF"/>
</g>
<g id="Ellipse 13" opacity="0.3" filter="url(#filter19_f_20300_14972)">
<circle cx="55.7274" cy="9.65615" r="0.753803" fill="#9A9AFF"/>
</g>
<g id="Ellipse 19" opacity="0.3" filter="url(#filter20_f_20300_14972)">
<circle cx="87.3886" cy="20.7108" r="0.753803" fill="#9A9AFF"/>
</g>
<g id="Ellipse 26" opacity="0.3" filter="url(#filter21_f_20300_14972)">
<circle cx="123.82" cy="15.1874" r="0.753803" fill="#9A9AFF"/>
</g>
<g id="Ellipse 36" opacity="0.3" filter="url(#filter22_f_20300_14972)">
<circle cx="134.876" cy="14.4335" r="0.753803" fill="#9A9AFF"/>
</g>
<g id="Ellipse 28" opacity="0.3" filter="url(#filter23_f_20300_14972)">
<circle cx="129.6" cy="6.38662" r="0.753803" fill="#9A9AFF"/>
</g>
<g id="Ellipse 14" opacity="0.3" filter="url(#filter24_f_20300_14972)">
<circle cx="41.6571" cy="14.9335" r="0.753803" fill="#9A9AFF"/>
</g>
<g id="Ellipse 31" opacity="0.3" filter="url(#filter25_f_20300_14972)">
<circle cx="4.21767" cy="17.9491" r="0.753803" fill="#9A9AFF"/>
</g>
<g id="Ellipse 34" opacity="0.3" filter="url(#filter26_f_20300_14972)">
<circle cx="12.2177" cy="6.88662" r="0.753803" fill="#9A9AFF"/>
</g>
<g id="Ellipse 17" opacity="0.3" filter="url(#filter27_f_20300_14972)">
<circle cx="50.2001" cy="3.6249" r="0.753803" fill="#9A9AFF"/>
</g>
</g>
<defs>
<filter id="filter0_f_20300_14972" x="-94" y="-88" width="361.085" height="168.176" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feGaussianBlur stdDeviation="32" result="effect1_foregroundBlur_20300_14972"/>
</filter>
<filter id="filter1_f_20300_14972" x="72.5633" y="2.61993" width="4.01991" height="4.02186" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feGaussianBlur stdDeviation="0.502536" result="effect1_foregroundBlur_20300_14972"/>
</filter>
<filter id="filter2_f_20300_14972" x="80.6029" y="6.13418" width="3.01484" height="3.01679" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feGaussianBlur stdDeviation="0.251268" result="effect1_foregroundBlur_20300_14972"/>
</filter>
<filter id="filter3_f_20300_14972" x="115.278" y="3.12246" width="3.01484" height="3.01679" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feGaussianBlur stdDeviation="0.251268" result="effect1_foregroundBlur_20300_14972"/>
</filter>
<filter id="filter4_f_20300_14972" x="63.2664" y="7.39727" width="3.01502" height="3.01405" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feGaussianBlur stdDeviation="0.502536" result="effect1_foregroundBlur_20300_14972"/>
</filter>
<filter id="filter5_f_20300_14972" x="94.9246" y="2.36602" width="3.01502" height="3.01405" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feGaussianBlur stdDeviation="0.502536" result="effect1_foregroundBlur_20300_14972"/>
</filter>
<filter id="filter6_f_20300_14972" x="74.5731" y="13.4285" width="3.01502" height="3.01405" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feGaussianBlur stdDeviation="0.502536" result="effect1_foregroundBlur_20300_14972"/>
</filter>
<filter id="filter7_f_20300_14972" x="103.218" y="3.36993" width="3.01502" height="3.01405" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feGaussianBlur stdDeviation="0.502536" result="effect1_foregroundBlur_20300_14972"/>
</filter>
<filter id="filter8_f_20300_14972" x="112.513" y="5.63165" width="3.01502" height="3.01405" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feGaussianBlur stdDeviation="0.502536" result="effect1_foregroundBlur_20300_14972"/>
</filter>
<filter id="filter9_f_20300_14972" x="108.242" y="13.9285" width="3.01502" height="3.01405" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feGaussianBlur stdDeviation="0.502536" result="effect1_foregroundBlur_20300_14972"/>
</filter>
<filter id="filter10_f_20300_14972" x="118.294" y="11.1629" width="3.01502" height="3.01405" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feGaussianBlur stdDeviation="0.502536" result="effect1_foregroundBlur_20300_14972"/>
</filter>
<filter id="filter11_f_20300_14972" x="92.5809" y="9.51055" width="3.01502" height="3.01405" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feGaussianBlur stdDeviation="0.502536" result="effect1_foregroundBlur_20300_14972"/>
</filter>
<filter id="filter12_f_20300_14972" x="153.47" y="10.6615" width="2.00995" height="2.00898" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feGaussianBlur stdDeviation="0.251268" result="effect1_foregroundBlur_20300_14972"/>
</filter>
<filter id="filter13_f_20300_14972" x="144.928" y="7.39727" width="3.01502" height="3.01405" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feGaussianBlur stdDeviation="0.502536" result="effect1_foregroundBlur_20300_14972"/>
</filter>
<filter id="filter14_f_20300_14972" x="22.5027" y="2.94024" width="3.01502" height="3.01405" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feGaussianBlur stdDeviation="0.502536" result="effect1_foregroundBlur_20300_14972"/>
</filter>
<filter id="filter15_f_20300_14972" x="-2.56659" y="12.1707" width="3.01502" height="3.01405" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feGaussianBlur stdDeviation="0.502536" result="effect1_foregroundBlur_20300_14972"/>
</filter>
<filter id="filter16_f_20300_14972" x="15.9334" y="14.9832" width="3.01502" height="3.01405" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feGaussianBlur stdDeviation="0.502536" result="effect1_foregroundBlur_20300_14972"/>
</filter>
<filter id="filter17_f_20300_14972" x="35.0502" y="5.63028" width="2.00995" height="2.00898" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feGaussianBlur stdDeviation="0.251268" result="effect1_foregroundBlur_20300_14972"/>
</filter>
<filter id="filter18_f_20300_14972" x="9.86563" y="13.2943" width="2.00995" height="2.00898" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feGaussianBlur stdDeviation="0.251268" result="effect1_foregroundBlur_20300_14972"/>
</filter>
<filter id="filter19_f_20300_14972" x="53.9686" y="7.89727" width="3.51795" height="3.51795" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feGaussianBlur stdDeviation="0.502536" result="effect1_foregroundBlur_20300_14972"/>
</filter>
<filter id="filter20_f_20300_14972" x="85.6297" y="18.952" width="3.51795" height="3.51795" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feGaussianBlur stdDeviation="0.502536" result="effect1_foregroundBlur_20300_14972"/>
</filter>
<filter id="filter21_f_20300_14972" x="122.061" y="13.4285" width="3.51795" height="3.51795" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feGaussianBlur stdDeviation="0.502536" result="effect1_foregroundBlur_20300_14972"/>
</filter>
<filter id="filter22_f_20300_14972" x="133.117" y="12.6746" width="3.51795" height="3.51795" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feGaussianBlur stdDeviation="0.502536" result="effect1_foregroundBlur_20300_14972"/>
</filter>
<filter id="filter23_f_20300_14972" x="127.842" y="4.62774" width="3.51795" height="3.51795" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feGaussianBlur stdDeviation="0.502536" result="effect1_foregroundBlur_20300_14972"/>
</filter>
<filter id="filter24_f_20300_14972" x="39.8982" y="13.1746" width="3.51795" height="3.51795" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feGaussianBlur stdDeviation="0.502536" result="effect1_foregroundBlur_20300_14972"/>
</filter>
<filter id="filter25_f_20300_14972" x="2.4588" y="16.1902" width="3.51795" height="3.51795" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feGaussianBlur stdDeviation="0.502536" result="effect1_foregroundBlur_20300_14972"/>
</filter>
<filter id="filter26_f_20300_14972" x="10.4588" y="5.12774" width="3.51795" height="3.51795" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feGaussianBlur stdDeviation="0.502536" result="effect1_foregroundBlur_20300_14972"/>
</filter>
<filter id="filter27_f_20300_14972" x="48.4412" y="1.86602" width="3.51795" height="3.51795" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feGaussianBlur stdDeviation="0.502536" result="effect1_foregroundBlur_20300_14972"/>
</filter>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 15 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 300 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.8 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 481 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 297 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 973 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

-38
View File
@@ -1,38 +0,0 @@
## App Integrations
### Amplitude
Analytics service used to collect data to help the dYdX Product team make informed product decisions.
<b>To use with dydxprotocol/v4-web:</b>
1. Amplitude account with API key.
2. Add API key in Github > Secrets and Variables > Actions as `AMPLITUDE_API_KEY`
3. In your deploy scripts add `pnpm run build:inject-amplitude` after your pnpm build / vite build command.
### Bugsnag
Error handling service used to collect handled/unhandled errors within the app. The information collected is used to help debug and alert the engineering team of crashes/unhandled errors within the app to improve stability.
<b>To use with dydxprotocol/v4-web:</b>
1. Bugsnag account with API key.
2. Add API key in Github > Secrets and Variables > Actions as `BUGSNAG_API_KEY`
3. In your deploy scripts add `pnpm run build:inject-bugsnag` after your pnpm build / vite build command.
4. If you are using with the Amplitude deployment scripts, your build command may look like the following: `pnpm build && pnpm run build:inject-amplitude && pnpm run build:inject-bugsnag`
### StatusPage
Service used to inform users of any status updates to our platform. These status updates are manually set and updated by the On Call engineers.
<b>To use with dydxprotocol/v4-web:</b>
1. StatusPage account and script URI
2. Add API key in Github > Secrets and Variables > Actions as `STATUS_PAGE_SCRIPT_URI`
3. In your deploy scripts add `pnpm run build:inject-statuspage` after your pnpm build / vite build command.
4. If you are using with the Amplitude deployment scripts, your build command may look like the following: `pnpm build && pnpm run build:inject-amplitude && pnpm run build:inject-statuspage`
### Intercom
Service used for live customer support (chat/inbox) as well as home for Help Articles.
<b>To use with dydxprotocol/v4-web:</b>
1. Create Intercom account
2. In Intercom UI
Getting started > Set up Messenger > will give you your API Key on Step 2
Customize Intercom Messenger by adding logo and brand colors
3. Add API key in Github > Secrets and Variables > Actions as `INTERCOM_APP_ID`
4. In your deploy scripts add `pnpm run build:inject-intercom` after your pnpm build / vite build command.
5. If you are using with the Amplitude deployment scripts, your build command may look like the following: `pnpm build && pnpm run build:inject-amplitude && pnpm run build:inject-intercom`
-19
View File
@@ -1,19 +0,0 @@
#!/bin/sh
source="dist/.well-known/apple-app-site-association"
if [ ! -f "$source" ]; then
echo "Error: $source file not found"
exit 1
fi
app_id=$IOS_APP_ID
if [ -z "$app_id" ]; then
echo "Error: IOS_APP_ID is empty"
exit 1
fi
if [[ "$OSTYPE" == "darwin"* ]]; then
sed -i '' "s/{PLACE_YOUR_APP_ID_HERE}/$app_id/g" $source
else
sed -i "s/{PLACE_YOUR_APP_ID_HERE}/$app_id/g" $source
fi
-76
View File
@@ -1,76 +0,0 @@
/* eslint-disable no-console */
import fs from 'fs/promises';
import path from 'path';
import { fileURLToPath } from 'url';
const INTERCOM_APP_ID = process.env.INTERCOM_APP_ID;
const currentPath = fileURLToPath(import.meta.url);
const projectRoot = path.dirname(currentPath);
const htmlFilePath = path.resolve(projectRoot, '../dist/index.html');
if (INTERCOM_APP_ID) {
try {
const html = await fs.readFile(htmlFilePath, 'utf-8');
const intercomScripts = `
<!-- Intercom -->
<script>
window.intercomSettings = {
api_base: 'https://api-iam.intercom.io',
app_id: '${INTERCOM_APP_ID}',
custom_launcher_selector: '.custom_intercom',
hide_default_launcher: true,
};
</script>
<script>
// We pre-filled your app ID in the widget URL: 'https://widget.intercom.io/widget/${INTERCOM_APP_ID}'
(function () {
var w = window;
var ic = w.Intercom;
if (typeof ic === 'function') {
ic('reattach_activator');
ic('update', w.intercomSettings);
} else {
var d = document;
var i = function () {
i.c(arguments);
};
i.q = [];
i.c = function (args) {
i.q.push(args);
};
w.Intercom = i;
var l = function () {
var s = d.createElement('script');
s.type = 'text/javascript';
s.async = true;
s.src = 'https://widget.intercom.io/widget/${INTERCOM_APP_ID}';
var x = d.getElementsByTagName('script')[0];
x.parentNode.insertBefore(s, x);
};
if (document.readyState === 'complete') {
l();
} else if (w.attachEvent) {
w.attachEvent('onload', l);
} else {
w.addEventListener('load', l, false);
}
}
})();
</script>
`;
const injectedHtml = html.replace(
'<div id="root"></div>',
`<div id="root"></div>\n${intercomScripts}\n`
);
await fs.writeFile(htmlFilePath, injectedHtml, 'utf-8');
console.log('Intercom scripts successfully injected.');
} catch (err) {
console.error('Error injecting Intercom scripts:', err);
}
}
+74 -105
View File
@@ -1,32 +1,31 @@
import { lazy, Suspense } from 'react';
import { Suspense } from 'react';
import { Navigate, Route, Routes } from 'react-router-dom';
import styled, { AnyStyledComponent, css } from 'styled-components';
import { WagmiConfig } from 'wagmi';
import { QueryClient, QueryClientProvider } from 'react-query';
import { GrazProvider } from 'graz';
import { AppRoute, DEFAULT_TRADE_ROUTE, MarketsRoute } from '@/constants/routes';
import { AppRoute, DEFAULT_TRADE_ROUTE } from '@/constants/routes';
import {
useBreakpoints,
useTokenConfigs,
useInitializePage,
useShouldShowFooter,
useAnalytics,
} from '@/hooks';
import { useBreakpoints, useInitializePage, useShouldShowFooter, useAnalytics } from '@/hooks';
import { DydxProvider } from '@/hooks/useDydxClient';
import { AccountsProvider } from '@/hooks/useAccounts';
import { AppThemeAndColorModeProvider } from '@/hooks/useAppThemeAndColorMode';
import { DialogAreaProvider, useDialogArea } from '@/hooks/useDialogArea';
import { LocaleProvider } from '@/hooks/useLocaleSeparators';
import { NotificationsProvider } from '@/hooks/useNotifications';
import { LocalNotificationsProvider } from '@/hooks/useLocalNotifications';
import { PotentialMarketsProvider } from '@/hooks/usePotentialMarkets';
import { RestrictionProvider } from '@/hooks/useRestrictions';
import { SubaccountProvider } from '@/hooks/useSubaccount';
import { DialogAreaProvider, useDialogArea } from './hooks/useDialogArea';
import { LocaleProvider } from './hooks/useLocaleSeparators';
import { NotificationsProvider } from './hooks/useNotifications';
import { SubaccountProvider } from './hooks/useSubaccount';
import { SquidProvider } from '@/hooks/useSquidRouter';
import { GuardedMobileRoute } from '@/components/GuardedMobileRoute';
import MarketsPage from '@/pages/markets/Markets';
import PortfolioPage from '@/pages/portfolio/Portfolio';
import { AlertsPage } from '@/pages/AlertsPage';
import ProfilePage from '@/pages/Profile';
import { SettingsPage } from '@/pages/settings/Settings';
import TradePage from '@/pages/trade/Trade';
import { RewardsPage } from '@/pages/rewards/RewardsPage';
import { HeaderDesktop } from '@/layout/Header/HeaderDesktop';
import { FooterDesktop } from '@/layout/Footer/FooterDesktop';
import { FooterMobile } from '@/layout/Footer/FooterMobile';
@@ -37,25 +36,12 @@ import { GlobalCommandDialog } from '@/views/dialogs/GlobalCommandDialog';
import { config } from '@/lib/wagmi';
import { breakpoints } from '@/styles';
import { GlobalStyle } from '@/styles/globalStyle';
import { layoutMixins } from '@/styles/layoutMixins';
import { LoadingSpace } from './components/Loading/LoadingSpinner';
import '@/styles/constants.css';
import '@/styles/fonts.css';
import '@/styles/web3modal.css';
const NewMarket = lazy(() => import('@/pages/markets/NewMarket'));
const MarketsPage = lazy(() => import('@/pages/markets/Markets'));
const PortfolioPage = lazy(() => import('@/pages/portfolio/Portfolio'));
const AlertsPage = lazy(() => import('@/pages/AlertsPage'));
const ProfilePage = lazy(() => import('@/pages/Profile'));
const SettingsPage = lazy(() => import('@/pages/settings/Settings'));
const TradePage = lazy(() => import('@/pages/trade/Trade'));
const RewardsPage = lazy(() => import('@/pages/rewards/RewardsPage'));
const TermsOfUsePage = lazy(() => import('@/pages/TermsOfUsePage'));
const PrivacyPolicyPage = lazy(() => import('@/pages/PrivacyPolicyPage'));
const queryClient = new QueryClient();
const Content = () => {
@@ -67,89 +53,74 @@ const Content = () => {
const { isTablet, isNotTablet } = useBreakpoints();
const isShowingHeader = isNotTablet;
const isShowingFooter = useShouldShowFooter();
const { chainTokenLabel } = useTokenConfigs();
return (
<>
<GlobalStyle />
<Styled.Content isShowingHeader={isShowingHeader} isShowingFooter={isShowingFooter}>
{isNotTablet && <HeaderDesktop />}
<Styled.Content isShowingHeader={isShowingHeader} isShowingFooter={isShowingFooter}>
{isNotTablet && <HeaderDesktop />}
<Styled.Main>
<Suspense fallback={<LoadingSpace id="main" />}>
<Routes>
<Route path={AppRoute.Trade}>
<Route path=":market" element={<TradePage />} />
<Route path={AppRoute.Trade} element={<TradePage />} />
</Route>
<Styled.Main>
<Suspense fallback={null}>
<Routes>
<Route path={AppRoute.Trade}>
<Route path=":market" element={<TradePage />} />
<Route path={AppRoute.Trade} element={<TradePage />} />
</Route>
<Route path={AppRoute.Markets} element={<MarketsPage />} />
{import.meta.env.MODE !== 'production' && (
<Route path={AppRoute.Rewards} element={<RewardsPage />} />
)}
{isTablet && (
<>
<Route path={AppRoute.Alerts} element={<AlertsPage />} />
<Route path={AppRoute.Profile} element={<ProfilePage />} />
<Route path={`${AppRoute.Settings}/*`} element={<SettingsPage />} />
</>
)}
<Route path={AppRoute.Markets}>
<Route path={MarketsRoute.New} element={<NewMarket />} />
<Route path={AppRoute.Markets} element={<MarketsPage />} />
</Route>
<Route path={`/${chainTokenLabel}`} element={<RewardsPage />} />
{isTablet && (
<>
<Route path={AppRoute.Alerts} element={<AlertsPage />} />
<Route path={AppRoute.Profile} element={<ProfilePage />} />
<Route path={`${AppRoute.Settings}/*`} element={<SettingsPage />} />
</>
)}
<Route element={<GuardedMobileRoute />}>
<Route path={`${AppRoute.Portfolio}/*`} element={<PortfolioPage />} />
</Route>
<Route path="*" element={<Navigate to={DEFAULT_TRADE_ROUTE} replace />} />
</Routes>
</Suspense>
</Styled.Main>
<Route element={<GuardedMobileRoute />}>
<Route path={`${AppRoute.Portfolio}/*`} element={<PortfolioPage />} />
</Route>
{isTablet ? <FooterMobile /> : <FooterDesktop />}
<Route path={AppRoute.Terms} element={<TermsOfUsePage />} />
<Route path={AppRoute.Privacy} element={<PrivacyPolicyPage />} />
<Styled.NotificationsToastArea />
<Route path="*" element={<Navigate to={DEFAULT_TRADE_ROUTE} replace />} />
</Routes>
</Suspense>
</Styled.Main>
<Styled.DialogArea ref={setDialogArea}>
<DialogManager />
</Styled.DialogArea>
{isTablet ? <FooterMobile /> : <FooterDesktop />}
<Styled.NotificationsToastArea />
<Styled.DialogArea ref={setDialogArea}>
<DialogManager />
</Styled.DialogArea>
<GlobalCommandDialog />
</Styled.Content>
</>
<GlobalCommandDialog />
</Styled.Content>
);
};
const wrapProvider = (Component: React.ComponentType<any>, props?: any) => {
// eslint-disable-next-line react/display-name
return ({ children }: { children: React.ReactNode }) => (
<Component {...props}>{children}</Component>
);
};
const providers = [
wrapProvider(QueryClientProvider, { client: queryClient }),
wrapProvider(GrazProvider),
wrapProvider(WagmiConfig, { config }),
wrapProvider(LocaleProvider),
wrapProvider(RestrictionProvider),
wrapProvider(DydxProvider),
wrapProvider(AccountsProvider),
wrapProvider(SubaccountProvider),
wrapProvider(LocalNotificationsProvider),
wrapProvider(NotificationsProvider),
wrapProvider(DialogAreaProvider),
wrapProvider(PotentialMarketsProvider),
wrapProvider(AppThemeAndColorModeProvider),
];
const App = () => {
return [...providers].reverse().reduce((children, Provider) => {
return <Provider>{children}</Provider>;
}, <Content />);
};
const App = () => (
<QueryClientProvider client={queryClient}>
<GrazProvider>
<WagmiConfig config={config}>
<LocaleProvider>
<DydxProvider>
<AccountsProvider>
<SubaccountProvider>
<NotificationsProvider>
<DialogAreaProvider>
<SquidProvider>
<Content />
</SquidProvider>
</DialogAreaProvider>
</NotificationsProvider>
</SubaccountProvider>
</AccountsProvider>
</DydxProvider>
</LocaleProvider>
</WagmiConfig>
</GrazProvider>
</QueryClientProvider>
);
const Styled: Record<string, AnyStyledComponent> = {};
@@ -221,9 +192,7 @@ Styled.NotificationsToastArea = styled(NotificationsToastArea)`
`;
Styled.DialogArea = styled.aside`
position: fixed;
height: 100%;
z-index: 1;
position: absolute;
inset: 0;
overflow: clip;
${layoutMixins.noPointerEvents}
-222
View File
@@ -1,222 +0,0 @@
[
{
"constant": true,
"inputs": [],
"name": "name",
"outputs": [
{
"name": "",
"type": "string"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": false,
"inputs": [
{
"name": "_spender",
"type": "address"
},
{
"name": "_value",
"type": "uint256"
}
],
"name": "approve",
"outputs": [
{
"name": "",
"type": "bool"
}
],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "totalSupply",
"outputs": [
{
"name": "",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": false,
"inputs": [
{
"name": "_from",
"type": "address"
},
{
"name": "_to",
"type": "address"
},
{
"name": "_value",
"type": "uint256"
}
],
"name": "transferFrom",
"outputs": [
{
"name": "",
"type": "bool"
}
],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "decimals",
"outputs": [
{
"name": "",
"type": "uint8"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [
{
"name": "_owner",
"type": "address"
}
],
"name": "balanceOf",
"outputs": [
{
"name": "balance",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "symbol",
"outputs": [
{
"name": "",
"type": "string"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": false,
"inputs": [
{
"name": "_to",
"type": "address"
},
{
"name": "_value",
"type": "uint256"
}
],
"name": "transfer",
"outputs": [
{
"name": "",
"type": "bool"
}
],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": true,
"inputs": [
{
"name": "_owner",
"type": "address"
},
{
"name": "_spender",
"type": "address"
}
],
"name": "allowance",
"outputs": [
{
"name": "",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"payable": true,
"stateMutability": "payable",
"type": "fallback"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"name": "owner",
"type": "address"
},
{
"indexed": true,
"name": "spender",
"type": "address"
},
{
"indexed": false,
"name": "value",
"type": "uint256"
}
],
"name": "Approval",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"name": "from",
"type": "address"
},
{
"indexed": true,
"name": "to",
"type": "address"
},
{
"indexed": false,
"name": "value",
"type": "uint256"
}
],
"name": "Transfer",
"type": "event"
}
]
-390
View File
@@ -1,390 +0,0 @@
[
{
"constant": true,
"inputs": [],
"name": "name",
"outputs": [{ "name": "", "type": "string" }],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": false,
"inputs": [{ "name": "_upgradedAddress", "type": "address" }],
"name": "deprecate",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": false,
"inputs": [
{ "name": "_spender", "type": "address" },
{ "name": "_value", "type": "uint256" }
],
"name": "approve",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "deprecated",
"outputs": [{ "name": "", "type": "bool" }],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": false,
"inputs": [{ "name": "_evilUser", "type": "address" }],
"name": "addBlackList",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "totalSupply",
"outputs": [{ "name": "", "type": "uint256" }],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": false,
"inputs": [
{ "name": "_from", "type": "address" },
{ "name": "_to", "type": "address" },
{ "name": "_value", "type": "uint256" }
],
"name": "transferFrom",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "upgradedAddress",
"outputs": [{ "name": "", "type": "address" }],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [{ "name": "", "type": "address" }],
"name": "balances",
"outputs": [{ "name": "", "type": "uint256" }],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "decimals",
"outputs": [{ "name": "", "type": "uint256" }],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "maximumFee",
"outputs": [{ "name": "", "type": "uint256" }],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "_totalSupply",
"outputs": [{ "name": "", "type": "uint256" }],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": false,
"inputs": [],
"name": "unpause",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": true,
"inputs": [{ "name": "_maker", "type": "address" }],
"name": "getBlackListStatus",
"outputs": [{ "name": "", "type": "bool" }],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [
{ "name": "", "type": "address" },
{ "name": "", "type": "address" }
],
"name": "allowed",
"outputs": [{ "name": "", "type": "uint256" }],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "paused",
"outputs": [{ "name": "", "type": "bool" }],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [{ "name": "who", "type": "address" }],
"name": "balanceOf",
"outputs": [{ "name": "", "type": "uint256" }],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": false,
"inputs": [],
"name": "pause",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "getOwner",
"outputs": [{ "name": "", "type": "address" }],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "owner",
"outputs": [{ "name": "", "type": "address" }],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "symbol",
"outputs": [{ "name": "", "type": "string" }],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": false,
"inputs": [
{ "name": "_to", "type": "address" },
{ "name": "_value", "type": "uint256" }
],
"name": "transfer",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": false,
"inputs": [
{ "name": "newBasisPoints", "type": "uint256" },
{ "name": "newMaxFee", "type": "uint256" }
],
"name": "setParams",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": false,
"inputs": [{ "name": "amount", "type": "uint256" }],
"name": "issue",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": false,
"inputs": [{ "name": "amount", "type": "uint256" }],
"name": "redeem",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": true,
"inputs": [
{ "name": "_owner", "type": "address" },
{ "name": "_spender", "type": "address" }
],
"name": "allowance",
"outputs": [{ "name": "remaining", "type": "uint256" }],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "basisPointsRate",
"outputs": [{ "name": "", "type": "uint256" }],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [{ "name": "", "type": "address" }],
"name": "isBlackListed",
"outputs": [{ "name": "", "type": "bool" }],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": false,
"inputs": [{ "name": "_clearedUser", "type": "address" }],
"name": "removeBlackList",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "MAX_UINT",
"outputs": [{ "name": "", "type": "uint256" }],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": false,
"inputs": [{ "name": "newOwner", "type": "address" }],
"name": "transferOwnership",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": false,
"inputs": [{ "name": "_blackListedUser", "type": "address" }],
"name": "destroyBlackFunds",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{ "name": "_initialSupply", "type": "uint256" },
{ "name": "_name", "type": "string" },
{ "name": "_symbol", "type": "string" },
{ "name": "_decimals", "type": "uint256" }
],
"payable": false,
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"anonymous": false,
"inputs": [{ "indexed": false, "name": "amount", "type": "uint256" }],
"name": "Issue",
"type": "event"
},
{
"anonymous": false,
"inputs": [{ "indexed": false, "name": "amount", "type": "uint256" }],
"name": "Redeem",
"type": "event"
},
{
"anonymous": false,
"inputs": [{ "indexed": false, "name": "newAddress", "type": "address" }],
"name": "Deprecate",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{ "indexed": false, "name": "feeBasisPoints", "type": "uint256" },
{ "indexed": false, "name": "maxFee", "type": "uint256" }
],
"name": "Params",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{ "indexed": false, "name": "_blackListedUser", "type": "address" },
{ "indexed": false, "name": "_balance", "type": "uint256" }
],
"name": "DestroyedBlackFunds",
"type": "event"
},
{
"anonymous": false,
"inputs": [{ "indexed": false, "name": "_user", "type": "address" }],
"name": "AddedBlackList",
"type": "event"
},
{
"anonymous": false,
"inputs": [{ "indexed": false, "name": "_user", "type": "address" }],
"name": "RemovedBlackList",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{ "indexed": true, "name": "owner", "type": "address" },
{ "indexed": true, "name": "spender", "type": "address" },
{ "indexed": false, "name": "value", "type": "uint256" }
],
"name": "Approval",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{ "indexed": true, "name": "from", "type": "address" },
{ "indexed": true, "name": "to", "type": "address" },
{ "indexed": false, "name": "value", "type": "uint256" }
],
"name": "Transfer",
"type": "event"
},
{ "anonymous": false, "inputs": [], "name": "Pause", "type": "event" },
{ "anonymous": false, "inputs": [], "name": "Unpause", "type": "event" }
]
-26
View File
@@ -1,26 +0,0 @@
import type { Story } from '@ladle/react';
import { Accordion as AccordionComponent, AccordionProps } from '@/components/Accordion';
import { StoryWrapper } from '.ladle/components';
export const Accordion: Story<AccordionProps> = (args) => {
return (
<StoryWrapper>
<AccordionComponent {...args} />
</StoryWrapper>
);
};
Accordion.args = {
items: [
{
header: 'Question 1?',
content: 'Answer 1.',
},
{
header: 'Question 2?',
content: 'Answer 2.',
},
],
};
-129
View File
@@ -1,129 +0,0 @@
import styled, { keyframes, type AnyStyledComponent } from 'styled-components';
import { Root, Item, Header, Trigger, Content } from '@radix-ui/react-accordion';
import { layoutMixins } from '@/styles/layoutMixins';
import { breakpoints } from '@/styles';
import { PlusIcon } from '@/icons';
export type AccordionItem = {
header: React.ReactNode;
content: React.ReactNode;
};
export type AccordionProps = {
items: AccordionItem[];
className?: string;
};
export const Accordion = ({ items, className }: AccordionProps) => (
<Styled.Root className={className} type="single" collapsible>
{items.map(({ header, content }, idx) => (
<Styled.Item key={idx} value={idx.toString()}>
<Header>
<Styled.Trigger>
{header}
<Styled.Icon>
<PlusIcon />
</Styled.Icon>
</Styled.Trigger>
</Header>
<Styled.Content>{content}</Styled.Content>
</Styled.Item>
))}
</Styled.Root>
);
const Styled: Record<string, AnyStyledComponent> = {};
Styled.Root = styled(Root)`
--accordion-paddingY: 1rem;
--accordion-paddingX: 1rem;
@media ${breakpoints.notTablet} {
--accordion-paddingX: 1.5rem;
}
> *:not(:last-child) {
border-bottom: var(--border-width) solid var(--border-color);
}
`;
Styled.Item = styled(Item)``;
Styled.Icon = styled.div`
display: inline-flex;
justify-content: center;
align-items: center;
width: 2.25rem;
height: 2.25rem;
--color-border: var(--color-layer-6);
color: var(--color-text-1);
background-color: var(--color-layer-5);
border: solid var(--border-width) var(--color-border);
border-radius: 50%;
font: var(--font-small-book);
svg {
height: 1.125em;
width: 1.125em;
}
`;
Styled.Trigger = styled(Trigger)`
${layoutMixins.spacedRow}
width: 100%;
padding: var(--accordion-paddingY) var(--accordion-paddingX);
gap: 0.5rem;
color: var(--color-text-1);
text-align: start;
&:hover {
${Styled.Icon} {
color: var(--color-text-2);
filter: brightness(var(--hover-filter-base));
}
}
svg {
color: var(--color-text-0);
transition: transform 0.3s var(--ease-out-expo);
}
&[data-state='open'] svg {
transform: rotate(45deg);
}
`;
Styled.Content = styled(Content)`
overflow: hidden;
margin: 0 var(--accordion-paddingX) var(--accordion-paddingY);
color: var(--color-text-0);
&[data-state='open'] {
animation: ${keyframes`
from {
height: 0;
}
to {
height: var(--radix-accordion-content-height);
}
`} 0.3s var(--ease-out-expo);
}
&[data-state='closed'] {
animation: ${keyframes`
from {
height: var(--radix-accordion-content-height);
}
to {
height: 0;
}
`} 0.1s var(--ease-in-expo);
}
`;
+3
View File
@@ -40,6 +40,9 @@ const AlertContainer = styled.div<StyleProps>`
case AlertType.Info: {
return css`
--alert-accent-color: var(--color-text-1);
--alert-default-background-opacity: 0.133; // Relative
// --alert-background: var(--color-layer-6); // Absolute
`;
}
case AlertType.Success: {
+2 -9
View File
@@ -16,7 +16,6 @@ const assetIcons = {
AVAX: '/currencies/avax.png',
BCH: '/currencies/bch.png',
BLUR: '/currencies/blur.png',
BONK: '/currencies/bonk.png',
BTC: '/currencies/btc.png',
CELO: '/currencies/celo.png',
COMP: '/currencies/comp.png',
@@ -46,7 +45,6 @@ const assetIcons = {
SOL: '/currencies/sol.png',
SUI: '/currencies/sui.png',
SUSHI: '/currencies/sushi.png',
TIA: '/currencies/tia.png',
TRX: '/currencies/trx.png',
UMA: '/currencies/uma.png',
UNI: '/currencies/uni.png',
@@ -73,13 +71,8 @@ export const AssetIcon = ({
}: {
symbol?: Nullable<string>;
className?: string;
}) => (
<Styled.Img
src={isAssetSymbol(symbol) ? assetIcons[symbol] : '/currencies/unavailable.png'}
className={className}
alt={symbol}
/>
);
}) =>
isAssetSymbol(symbol) ? <Styled.Img src={assetIcons[symbol]} className={className} /> : null;
const Styled: Record<string, AnyStyledComponent> = {};
+2 -2
View File
@@ -134,8 +134,8 @@ const ButtonStyle = css<StyleProps>`
--button-textColor: var(--color-text-0);
--button-backgroundColor: transparent;
--button-active-filter: brightness(var(--active-filter));
--button-hover-filter: brightness(var(--hover-filter-base));
--button-active-filter: brightness(0.9);
--button-hover-filter: brightness(1.1);
--button-hover-textColor: var(--button-textColor);
--button-radius: 0.5em;
+7 -11
View File
@@ -61,7 +61,7 @@ export const Button = forwardRef<HTMLButtonElement | HTMLAnchorElement, ButtonPr
return (
<StyledBaseButton
disabled={state[ButtonState.Disabled] || state[ButtonState.Loading]}
disabled={state[ButtonState.Disabled]}
{...{ ref, action, size, shape, state, ...otherProps }}
>
{
@@ -89,10 +89,9 @@ const buttonActionVariants = {
--button-border: solid var(--border-width) var(--color-border);
`,
[ButtonAction.Primary]: css`
--button-textColor: var(--color-text-button);
--button-textColor: var(--color-text-2);
--button-backgroundColor: var(--color-accent);
--button-border: solid var(--border-width) var(--color-border-white);
--button-hover-filter: brightness(var(--hover-filter-variant));
`,
[ButtonAction.Secondary]: css`
@@ -102,17 +101,15 @@ const buttonActionVariants = {
`,
[ButtonAction.Create]: css`
--button-textColor: var(--color-text-button);
--button-backgroundColor: var(--color-green);
--button-textColor: var(--color-text-2);
--button-backgroundColor: var(--color-positive);
--button-border: solid var(--border-width) var(--color-border-white);
--button-hover-filter: brightness(var(--hover-filter-variant));
`,
[ButtonAction.Destroy]: css`
--button-textColor: var(--color-text-button);
--button-backgroundColor: var(--color-red);
--button-textColor: var(--color-text-2);
--button-backgroundColor: var(--color-negative);
--button-border: solid var(--border-width) var(--color-border-white);
--button-hover-filter: brightness(var(--hover-filter-variant));
`,
[ButtonAction.Navigation]: css`
@@ -122,10 +119,9 @@ const buttonActionVariants = {
`,
[ButtonAction.Reset]: css`
--button-textColor: var(--color-red);
--button-textColor: var(--color-negative);
--button-backgroundColor: var(--color-layer-3);
--button-border: solid var(--border-width) var(--color-border-red);
--button-hover-filter: brightness(var(--hover-filter-variant));
`,
};
-13
View File
@@ -1,13 +0,0 @@
import { forwardRef } from 'react';
type CanvasProps = {
className?: string;
width: number | string;
height: number | string;
};
export const Canvas = forwardRef<HTMLCanvasElement, CanvasProps>(
({ className, width, height }, canvasRef) => (
<canvas ref={canvasRef} className={className} width={width} height={height} />
)
);
+3 -13
View File
@@ -5,7 +5,7 @@ import { Checkbox, CheckboxProps } from '@/components/Checkbox';
import { StoryWrapper } from '.ladle/components';
export const CheckboxStory: Story<CheckboxProps> = (args) => {
export const Checkboxes: Story<CheckboxProps> = (args) => {
const [isChecked, setIsChecked] = useState(false);
return (
@@ -13,20 +13,10 @@ export const CheckboxStory: Story<CheckboxProps> = (args) => {
<Checkbox
{...args}
checked={isChecked}
onCheckedChange={setIsChecked}
id="story-checkbox"
label="example label"
onClick={(e: React.ChangeEvent) => setIsChecked((e.target as HTMLInputElement).checked)}
/>
</StoryWrapper>
);
};
CheckboxStory.args = {};
CheckboxStory.argTypes = {
disabled: {
options: [true, false],
control: { type: 'select' },
defaultValue: false,
}
}
Checkboxes.args = {};
+70 -69
View File
@@ -1,15 +1,10 @@
import styled, { css, type AnyStyledComponent } from 'styled-components';
import { Root, Indicator } from '@radix-ui/react-checkbox';
import { CheckIcon } from '@radix-ui/react-icons';
import styled, { type AnyStyledComponent } from 'styled-components';
import { layoutMixins } from '@/styles/layoutMixins';
import { breakpoints } from '@/styles';
type ElementProps = {
checked: boolean;
onCheckedChange: (checked: boolean) => void;
id?: string;
label?: React.ReactNode;
disabled?: boolean;
onClick: (e: React.ChangeEvent) => void;
};
type StyleProps = {
@@ -18,79 +13,85 @@ type StyleProps = {
export type CheckboxProps = ElementProps & StyleProps;
export const Checkbox: React.FC<CheckboxProps> = ({
checked,
className,
onCheckedChange,
id,
label,
disabled,
}) => (
<Styled.Container>
<Styled.Root
className={className}
checked={checked}
disabled={disabled}
onCheckedChange={onCheckedChange}
id={id}
>
<Styled.Indicator>
<CheckIcon />
</Styled.Indicator>
</Styled.Root>
{label && (
<Styled.Label disabled={disabled} htmlFor={id}>
{label}
</Styled.Label>
)}
</Styled.Container>
);
export const Checkbox: React.FC<CheckboxProps> = ({ checked, className, onClick }) => {
return (
<Styled.CheckboxWrapper className={className}>
<Styled.Checkbox
type="checkbox"
checked={checked}
onChange={onClick}
/>
<Styled.CustomCheckbox />
</Styled.CheckboxWrapper>
);
};
const Styled: Record<string, AnyStyledComponent> = {};
Styled.Container = styled.div`
${layoutMixins.row}
gap: 1ch;
font: var(--font-small-book);
`;
Styled.CustomCheckbox = styled.span`
width: 1.25em;
height: 1.25em;
Styled.Root = styled(Root)`
--checkbox-backgroundColor: var(--color-layer-0);
--checkbox-borderColor: var(--color-border);
min-width: 1.25rem;
height: 1.25rem;
border-radius: 0.375rem;
border: var(--border-width) solid var(--checkbox-borderColor);
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
background-color: var(--checkbox-backgroundColor);
&[data-state='checked'] {
--checkbox-backgroundColor: var(--color-accent);
&::after {
position: absolute;
content: '';
top: 0.25em;
left: 0.4375em;
width: 0.3125em;
height: 0.5em;
border: solid var(--checkbox-checkColor);
border-width: 0 0.125em 0.125em 0;
border-radius: 0.0625em;
opacity: 0;
transform: rotate(0deg) scale(0);
transition: opacity 0.2s ease-in-out, transform 0.2s ease-in-out;
}
&[data-disabled] {
cursor: not-allowed;
--checkbox-backgroundColor: var(--color-layer-1);
@media ${breakpoints.tablet} {
width: 1.5em;
height: 1.5em;
&::after {
top: 0.25em;
left: 0.4375em;
width: 0.4375em;
height: 0.625em;
}
}
`;
Styled.Indicator = styled(Indicator)`
Styled.CheckboxWrapper = styled.div`
--checkbox-backgroundColor: var(--color-layer-1);
--checkbox-checkColor: var(--color-text-1);
display: flex;
align-items: center;
justify-content: center;
color: var(--color-text-button);
`;
Styled.Label = styled.label<{ disabled?: boolean }>`
border-radius: 0.25em;
overflow: hidden;
position: relative;
cursor: pointer;
color: var(--color-text-2);
${({ disabled }) =>
disabled &&
css`
cursor: not-allowed;
color: var(--color-text-0);
`}
> input:checked ~ ${Styled.CustomCheckbox}::after {
opacity: 1;
transform: rotate(40deg) scale(1);
}
`;
Styled.Checkbox = styled.input`
width: 1.25em;
height: 1.25em;
z-index: 1;
cursor: pointer;
opacity: 0;
@media ${breakpoints.tablet} {
width: 1.5em;
height: 1.5em;
}
`;
+6 -5
View File
@@ -1,13 +1,16 @@
import type { Story } from '@ladle/react';
import { Collapsible, type CollapsibleProps } from '@/components/Collapsible';
import { Collapsible } from '@/components/Collapsible';
import { StoryWrapper } from '.ladle/components';
import { IconName } from './Icon';
export const CollapsibleStory: Story<CollapsibleProps> = (args) => (
export const CollapsibleStory: Story<Parameters<typeof Collapsible>> = (args) => (
<StoryWrapper>
<Collapsible {...args}>
<Collapsible
label="Collapsible List of Items"
{...args}
>
<ul>
<li>Collapsible Item 1</li>
<li>Collapsible Item 2</li>
@@ -20,8 +23,6 @@ export const CollapsibleStory: Story<CollapsibleProps> = (args) => (
CollapsibleStory.args = {
disabled: false,
withTrigger: true,
label: 'Collapsible List of Items',
};
CollapsibleStory.argTypes = {
+19 -25
View File
@@ -14,7 +14,6 @@ type ElementProps = {
label: React.ReactNode;
triggerIcon?: IconName;
children: React.ReactNode;
withTrigger?: boolean;
};
type StyleProps = {
@@ -24,8 +23,6 @@ type StyleProps = {
fullWidth?: boolean;
};
export type CollapsibleProps = ElementProps & StyleProps;
export const Collapsible = ({
defaultOpen = false,
disabled,
@@ -38,29 +35,26 @@ export const Collapsible = ({
triggerIconSide = 'left',
fullWidth,
className,
withTrigger = true,
}: CollapsibleProps) => (
}: ElementProps & StyleProps) => (
<Styled.Root defaultOpen={defaultOpen} open={open} onOpenChange={onOpenChange}>
{withTrigger && (
<Styled.Trigger className={className} disabled={disabled}>
{triggerIconSide === 'right' && (
<>
{label}
{fullWidth && <HorizontalSeparatorFiller />}
</>
)}
<Styled.Trigger className={className} disabled={disabled}>
{triggerIconSide === 'right' && (
<>
{label}
{fullWidth && <HorizontalSeparatorFiller />}
</>
)}
<Styled.TriggerIcon>
<Icon iconName={triggerIcon} />
</Styled.TriggerIcon>
{triggerIconSide === 'left' && (
<>
{fullWidth && <HorizontalSeparatorFiller />}
{label}
</>
)}
</Styled.Trigger>
)}
<Styled.TriggerIcon>
<Icon iconName={triggerIcon} />
</Styled.TriggerIcon>
{triggerIconSide === 'left' && (
<>
{fullWidth && <HorizontalSeparatorFiller />}
{label}
</>
)}
</Styled.Trigger>
<Styled.Content $transitionDuration={transitionDuration}>{children}</Styled.Content>
</Styled.Root>
);
@@ -86,7 +80,7 @@ Styled.TriggerIcon = styled.span`
width: var(--trigger-icon-width);
display: inline-flex;
transition: rotate 0.3s var(--ease-out-expo);
transition: transform 0.3s var(--ease-out-expo);
color: var(--trigger-icon-color);
${Styled.Trigger}[data-state='open'] & {
+5 -8
View File
@@ -31,12 +31,10 @@ type ElementProps<TabItemsValue> = {
};
type StyleProps = {
className?: string;
fullWidthTabs?: boolean;
className?: string;
};
export type CollapsibleTabsProps<TabItemsValue> = ElementProps<TabItemsValue> & StyleProps;
export const CollapsibleTabs = <TabItemsValue extends string>({
defaultValue,
items,
@@ -45,9 +43,8 @@ export const CollapsibleTabs = <TabItemsValue extends string>({
onOpenChange,
fullWidthTabs,
className,
}: CollapsibleTabsProps<TabItemsValue>) => {
}: ElementProps<TabItemsValue> & StyleProps) => {
const [value, setValue] = useState(defaultValue);
const currentItem = items.find((item) => item.value === value);
@@ -89,8 +86,8 @@ export const CollapsibleTabs = <TabItemsValue extends string>({
</Styled.Header>
<Styled.CollapsibleContent>
{items.map(({ asChild, value, content }) => (
<Styled.TabsContent key={value} asChild={asChild} value={value}>
{items.map(({ value, content }) => (
<Styled.TabsContent key={value} value={value}>
{content}
</Styled.TabsContent>
))}
@@ -222,12 +219,12 @@ Styled.Header = styled.header`
Styled.CollapsibleContent = styled(CollapsibleContent)`
${layoutMixins.stack}
${layoutMixins.perspectiveArea}
box-shadow: none;
`;
Styled.IconButton = styled(IconButton)`
--button-icon-size: 1em;
${Styled.CollapsibleRoot}[data-state='closed'] & {
rotate: -0.5turn;
}
+17 -49
View File
@@ -3,49 +3,31 @@ import styled, { AnyStyledComponent } from 'styled-components';
import { type MenuConfig } from '@/constants/menus';
import { Dialog, DialogPlacement, type DialogProps } from '@/components/Dialog';
import { ComboboxMenu, type ComboboxMenuProps } from '@/components/ComboboxMenu';
import { Dialog, DialogPlacement } from '@/components/Dialog';
import { ComboboxMenu } from '@/components/ComboboxMenu';
type ElementProps<MenuItemValue extends string | number, MenuGroupValue extends string | number> = {
isOpen?: boolean;
setIsOpen?: (open: boolean) => void;
title?: React.ReactNode;
description?: React.ReactNode;
slotTrigger?: React.ReactNode;
slotHeaderInner?: React.ReactNode;
slotFooter?: React.ReactNode;
children?: React.ReactNode;
items: MenuConfig<MenuItemValue, MenuGroupValue>;
onItemSelected?: () => void;
inputPlaceholder?: string;
slotEmpty?: React.ReactNode;
};
type StyleProps = {
placement: DialogPlacement,
className?: string;
};
type PickComboxMenuProps<
MenuItemValue extends string | number,
MenuGroupValue extends string | number
> = Pick<
ComboboxMenuProps<MenuItemValue, MenuGroupValue>,
| 'inputPlaceholder'
| 'onItemSelected'
| 'slotEmpty'
| 'withItemBorders'
| 'withSearch'
| 'withStickyLayout'
>;
type PickDialogProps = Pick<
DialogProps,
| 'description'
| 'isOpen'
| 'placement'
| 'setIsOpen'
| 'slotHeaderInner'
| 'slotTrigger'
| 'slotFooter'
| 'preventClose'
>;
export const ComboboxDialogMenu = <
MenuItemValue extends string | number,
MenuGroupValue extends string | number
>({
export const ComboboxDialogMenu = <MenuItemValue extends string | number, MenuGroupValue extends string | number>({
isOpen = false,
setIsOpen,
title,
@@ -58,18 +40,11 @@ export const ComboboxDialogMenu = <
onItemSelected,
inputPlaceholder,
slotEmpty,
withItemBorders,
withSearch,
withStickyLayout = true,
children,
placement = DialogPlacement.Default,
preventClose,
className,
}: ElementProps<MenuItemValue, MenuGroupValue> &
PickComboxMenuProps<MenuItemValue, MenuGroupValue> &
PickDialogProps &
StyleProps) => (
}: ElementProps<MenuItemValue, MenuGroupValue> & StyleProps) => (
// TODO: sub-menu state management
<Styled.Dialog
isOpen={isOpen}
@@ -79,8 +54,8 @@ export const ComboboxDialogMenu = <
slotHeaderInner={slotHeaderInner}
slotTrigger={slotTrigger}
slotFooter={slotFooter}
placement={placement}
preventClose={preventClose}
className={className}
>
<Styled.ComboboxMenu
@@ -89,9 +64,7 @@ export const ComboboxDialogMenu = <
title={title}
inputPlaceholder={inputPlaceholder}
slotEmpty={slotEmpty}
withItemBorders={withItemBorders}
withSearch={withSearch}
withStickyLayout={withStickyLayout}
withStickyLayout
/>
{children}
</Styled.Dialog>
@@ -102,8 +75,6 @@ const Styled: Record<string, AnyStyledComponent> = {};
Styled.Dialog = styled(Dialog)`
/* Params */
--comboboxDialogMenu-backgroundColor: var(--color-layer-2);
--comboboxDialogMenu-item-gap: 0.5rem;
--comboboxDialogMenu-item-padding: 0.5rem 1rem;
/* Overrides */
& {
@@ -122,12 +93,9 @@ Styled.Dialog = styled(Dialog)`
/* Net 0 sticky top inset (let stickyArea1 header stick to top) */
--stickyArea0-topGap: calc(-1 * var(--stickyArea0-topHeight));
overflow-x: clip;
}
`;
Styled.ComboboxMenu = styled(ComboboxMenu)`
--comboboxMenu-backgroundColor: var(--comboboxDialogMenu-backgroundColor);
--comboboxMenu-item-gap: var(--comboboxDialogMenu-item-gap);
--comboboxMenu-item-padding: var(--comboboxDialogMenu-item-padding);
`;
+39 -85
View File
@@ -2,13 +2,13 @@ import { Fragment, type ReactNode, useState } from 'react';
import styled, { type AnyStyledComponent, css } from 'styled-components';
import { Command } from 'cmdk';
import { MenuItem, type MenuConfig } from '@/constants/menus';
import { type MenuConfig } from '@/constants/menus';
import { popoverMixins } from '@/styles/popoverMixins';
import { layoutMixins } from '@/styles/layoutMixins';
import { Tag } from '@/components/Tag';
type ElementProps<MenuItemValue extends string | number, MenuGroupValue extends string | number> = {
type ElementProps<MenuItemValue extends string, MenuGroupValue extends string> = {
items: MenuConfig<MenuItemValue, MenuGroupValue>;
onItemSelected?: () => void;
@@ -20,15 +20,9 @@ type ElementProps<MenuItemValue extends string | number, MenuGroupValue extends
type StyleProps = {
className?: string;
withItemBorders?: boolean;
withStickyLayout?: boolean;
};
export type ComboboxMenuProps<
MenuItemValue extends string | number,
MenuGroupValue extends string | number
> = ElementProps<MenuItemValue, MenuGroupValue> & StyleProps;
export const ComboboxMenu = <MenuItemValue extends string, MenuGroupValue extends string>({
items,
onItemSelected,
@@ -39,36 +33,38 @@ export const ComboboxMenu = <MenuItemValue extends string, MenuGroupValue extend
withSearch = true,
className,
withItemBorders,
withStickyLayout,
}: ComboboxMenuProps<MenuItemValue, MenuGroupValue>) => {
}: ElementProps<MenuItemValue, MenuGroupValue> & StyleProps) => {
const [highlightedCommand, setHighlightedCommand] = useState<MenuItemValue>();
const [searchValue, setSearchValue] = useState('');
// const inputRef = useRef<HTMLInputElement | null>(null);
// console.log({ commandValue: highlightedCommand });
// useEffect(() => {
// inputRef?.current?.focus();
// }, []);
return (
<Styled.Command
label={title}
// value={highlightedCommand}
// onValueChange={setHighlightedCommand}
filter={(value: string, search: string) =>
value.replace(/ /g, '').toLowerCase().includes(search.replace(/ /g, '').toLowerCase())
? 1
: 0
}
filter={(value: string, search: string) => {
if (value.replace(/ /g, '').includes(search.replace(/ /g, ''))) return 1;
return 0;
}}
className={className}
$withStickyLayout={withStickyLayout}
>
{withSearch && (
<Styled.Header $withStickyLayout={withStickyLayout}>
<Styled.Input
/**
* Mobile Issue: Search Input will always trigger mobile keyboard drawer. There is no fix.
* https://github.com/pacocoursey/cmdk/issues/127
*/
autoFocus
// ref={inputRef}
type="search"
value={searchValue}
onValueChange={setSearchValue}
autoFocus
placeholder={inputPlaceholder}
/>
</Styled.Header>
@@ -79,7 +75,6 @@ export const ComboboxMenu = <MenuItemValue extends string, MenuGroupValue extend
<Styled.Group
key={group.group}
heading={group.groupLabel}
$withItemBorders={withItemBorders}
$withStickyLayout={withStickyLayout}
>
{group.items.map((item) => (
@@ -87,9 +82,7 @@ export const ComboboxMenu = <MenuItemValue extends string, MenuGroupValue extend
<Styled.Item
// value={item.value} // search by both value and textContent
// value={[group.groupLabel, item.label, item.tag].filter(Boolean).join('|')} // exclude item.value from searchable terms (not guaranteed to be unique)
value={[group.groupLabel, item.value, item.description, item.label, item.tag]
.filter(Boolean)
.join('|')}
value={[group.groupLabel, item.value, item.description, item.label, item.tag].filter(Boolean).join('|')}
onSelect={() => {
if (item.subitems) {
// open submenu
@@ -99,31 +92,22 @@ export const ComboboxMenu = <MenuItemValue extends string, MenuGroupValue extend
}
}}
disabled={item.disabled}
$withItemBorders={withItemBorders}
>
{
<>
{item.slotBefore}
{item.slotCustomContent ?? (
<Styled.ItemLabel>
<span>
{typeof item.label === 'string'
? `${item.label}${item.subitems?.length ? '…' : ''}`
: item.label}
{item.tag && (
<>
{' '}
<Tag>{item.tag}</Tag>
</>
)}
</span>
{item.description && <span>{item.description}</span>}
</Styled.ItemLabel>
{item.slotBefore}
<Styled.ItemLabel>
<span>
{`${item.label}${item.subitems?.length ? '…' : ''}`}
{item.tag && (
<>
{' '}
<Tag>{item.tag}</Tag>
</>
)}
{item.slotAfter}
{item.subitems && '→'}
</>
}
</span>
{item.description && <span>{item.description}</span>}
</Styled.ItemLabel>
{item.slotAfter}
{item.subitems && '→'}
</Styled.Item>
{searchValue &&
@@ -132,21 +116,12 @@ export const ComboboxMenu = <MenuItemValue extends string, MenuGroupValue extend
<Styled.Item
// value={subitem.value} // search by both value and textContent
// value={[group.groupLabel, item.label, subitem.label, subitem.tag].filter(Boolean).join('|')}
value={[
group.groupLabel,
item.value,
item.description,
item.label,
item.tag,
]
.filter(Boolean)
.join('|')}
value={[group.groupLabel, item.value, item.description, item.label, item.tag].filter(Boolean).join('|')}
onSelect={() => {
subitem.onSelect?.(subitem.value);
onItemSelected?.();
}}
disabled={subitem.disabled}
$withItemBorders={withItemBorders}
>
{subitem.slotBefore}
<Styled.ItemLabel>
@@ -179,17 +154,13 @@ const Styled: Record<string, AnyStyledComponent> = {};
Styled.Command = styled(Command)<{ $withStickyLayout?: boolean }>`
--comboboxMenu-backgroundColor: var(--color-layer-2);
--comboboxMenu-input-backgroundColor: var(--color-layer-3);
--comboboxMenu-input-height: 2.5rem;
--comboboxMenu-item-checked-backgroundColor: ;
--comboboxMenu-item-checked-textColor: ;
--comboboxMenu-item-highlighted-backgroundColor: var(--color-layer-3);
--comboboxMenu-item-highlighted-textColor: var(--color-text-1);
--comboboxMenu-item-backgroundColor: ;
--comboboxMenu-item-gap: 0.5rem;
--comboboxMenu-item-padding: 0.5em 1em;
display: grid;
align-content: start;
@@ -197,7 +168,7 @@ Styled.Command = styled(Command)<{ $withStickyLayout?: boolean }>`
background-color: var(--comboboxMenu-backgroundColor);
border-radius: inherit;
input:focus-visible {
input:focus {
outline: none;
}
@@ -238,7 +209,7 @@ Styled.Input = styled(Command.Input)`
gap: 0.5rem;
`;
Styled.Group = styled(Command.Group)<{ $withItemBorders?: boolean; $withStickyLayout?: boolean }>`
Styled.Group = styled(Command.Group)<{ $withStickyLayout?: boolean }>`
color: var(--color-text-0);
> [cmdk-group-heading] {
@@ -255,21 +226,12 @@ Styled.Group = styled(Command.Group)<{ $withItemBorders?: boolean; $withStickyLa
> [cmdk-group-heading] {
${layoutMixins.stickyHeader}
z-index: 2;
}
> [cmdk-group-items] {
${layoutMixins.stickyArea3}
}
`}
${({ $withItemBorders }) =>
$withItemBorders &&
css`
> [cmdk-group-items] {
padding: var(--border-width) 0;
}
`}
`;
Styled.List = styled(Command.List)<{ $withStickyLayout?: boolean }>`
@@ -296,30 +258,24 @@ Styled.List = styled(Command.List)<{ $withStickyLayout?: boolean }>`
`}
`;
Styled.Item = styled(Command.Item)<{ $withItemBorders?: boolean }>`
${layoutMixins.scrollSnapItem}
Styled.Item = styled(Command.Item)`
${popoverMixins.item}
--item-checked-backgroundColor: var(--comboboxMenu-item-checked-backgroundColor);
--item-checked-textColor: var(--comboboxMenu-item-checked-textColor);
--item-highlighted-textColor: var(--comboboxMenu-item-highlighted-textColor);
--item-gap: var(--comboboxMenu-item-gap);
--item-padding: var(--comboboxMenu-item-padding);
${layoutMixins.scrollSnapItem}
background-color: var(--comboboxMenu-backgroundColor, inherit);
display: flex;
align-items: center;
gap: 0.5rem;
&[aria-disabled='true'] {
&[aria-disabled="true"] {
opacity: 0.75;
cursor: not-allowed;
}
${({ $withItemBorders }) =>
$withItemBorders &&
css`
${layoutMixins.withOuterBorder}
`}
`;
Styled.ItemLabel = styled.div`
@@ -338,8 +294,6 @@ Styled.ItemLabel = styled.div`
opacity: 0.8;
}
}
min-width: 0;
`;
Styled.Empty = styled(Command.Empty)`
+1
View File
@@ -11,5 +11,6 @@ export const DetachedScrollableSection = styled.section`
export const AttachedExpandingSection = styled.section`
${layoutMixins.contentSectionAttached}
${layoutMixins.expandingColumnWithHeader}
gap: var(--border-width);
`;
+3 -3
View File
@@ -15,10 +15,10 @@ CopyButtonStory.args = {
};
CopyButtonStory.argTypes = {
buttonType: {
options: ["text", "icon", "default"],
shownAsText: {
options: [true, false],
control: { type: 'select' },
defaultValue: "default",
defaultValue: false,
},
children: {
options: ['some text to copy'],
+5 -42
View File
@@ -9,21 +9,14 @@ import { layoutMixins } from '@/styles/layoutMixins';
import { Button, ButtonProps } from './Button';
import { Icon, IconName } from './Icon';
import { IconButton } from './IconButton';
import { WithTooltip } from './WithTooltip';
export type CopyButtonProps = {
value?: string;
buttonType?: 'text' | 'icon' | 'default';
shownAsText?: boolean;
children?: React.ReactNode;
} & ButtonProps;
export const CopyButton = ({
value,
buttonType = 'default',
children,
...buttonProps
}: CopyButtonProps) => {
export const CopyButton = ({ value, shownAsText, children, ...buttonProps }: CopyButtonProps) => {
const stringGetter = useStringGetter();
const [copied, setCopied] = useState(false);
@@ -35,23 +28,11 @@ export const CopyButton = ({
setTimeout(() => setCopied(false), 500);
};
return buttonType === 'text' ? (
return shownAsText ? (
<Styled.InlineRow onClick={onCopy} copied={copied}>
{children}
<Styled.Icon copied={copied} iconName={copied ? IconName.Check : IconName.Copy} />
<Icon iconName={IconName.Copy} />
</Styled.InlineRow>
) : buttonType === 'icon' ? (
<WithTooltip
tooltipString={stringGetter({ key: copied ? STRING_KEYS.COPIED : STRING_KEYS.COPY })}
>
<Styled.IconButton
{...buttonProps}
copied={copied}
action={ButtonAction.Base}
iconName={copied ? IconName.Check : IconName.Copy}
onClick={onCopy}
/>
</WithTooltip>
) : (
<Button
{...buttonProps}
@@ -77,26 +58,8 @@ Styled.InlineRow = styled.div<{ copied: boolean }>`
`
: css`
&:hover {
filter: brightness(var(--hover-filter-base));
filter: brightness(1.1);
text-decoration: underline;
}
`}
`;
Styled.Icon = styled(Icon)<{ copied: boolean }>`
${({ copied }) =>
copied &&
css`
color: var(--color-success);
`}
`;
Styled.IconButton = styled(IconButton)<{ copied: boolean }>`
${({ copied }) =>
copied &&
css`
svg {
color: var(--color-success);
}
`}
`;
+34 -43
View File
@@ -38,7 +38,6 @@ type ElementProps = {
slotTrigger?: React.ReactNode;
slotHeaderInner?: React.ReactNode;
slotFooter?: React.ReactNode;
withClose?: boolean;
};
type StyleProps = {
@@ -47,11 +46,8 @@ type StyleProps = {
hasHeaderBorder?: boolean;
children?: React.ReactNode;
className?: string;
withAnimation?: boolean;
};
export type DialogProps = ElementProps & StyleProps;
const DialogPortal = ({
withPortal,
container,
@@ -81,14 +77,12 @@ export const Dialog = ({
slotTrigger,
slotHeaderInner,
slotFooter,
withClose = true,
placement = DialogPlacement.Default,
portalContainer,
hasHeaderBorder = false,
withAnimation = false,
children,
className,
}: DialogProps) => {
}: ElementProps & StyleProps) => {
const closeButtonRef = useRef<HTMLButtonElement>();
const showOverlay = ![DialogPlacement.Inline, DialogPlacement.FullScreen].includes(placement);
@@ -96,7 +90,10 @@ export const Dialog = ({
return (
<Root modal={showOverlay} open={isOpen} onOpenChange={setIsOpen}>
{slotTrigger && <Trigger asChild>{slotTrigger}</Trigger>}
<DialogPortal withPortal={placement !== DialogPlacement.Inline} container={portalContainer}>
<DialogPortal
withPortal={placement !== DialogPlacement.Inline}
container={portalContainer}
>
{showOverlay && <Styled.Overlay />}
<Styled.Container
placement={placement}
@@ -109,7 +106,6 @@ export const Dialog = ({
e.preventDefault();
}
}}
$withAnimation={withAnimation}
>
<Styled.Header $withBorder={hasHeaderBorder}>
<Styled.HeaderTopRow>
@@ -119,7 +115,7 @@ export const Dialog = ({
{title && <Styled.Title>{title}</Styled.Title>}
{!preventClose && withClose && (
{!preventClose && (
<Styled.Close ref={closeButtonRef}>
<Icon iconName={IconName.Close} />
</Styled.Close>
@@ -173,7 +169,7 @@ Styled.Overlay = styled(Overlay)`
}
`;
Styled.Container = styled(Content)<{ placement: DialogPlacement; $withAnimation?: boolean }>`
Styled.Container = styled(Content)<{ placement: DialogPlacement }>`
/* Params */
--dialog-inset: 1rem;
--dialog-width: 30rem;
@@ -229,7 +225,7 @@ Styled.Container = styled(Content)<{ placement: DialogPlacement; $withAnimation?
outline: none;
${({ placement, $withAnimation }) =>
${({ placement }) =>
({
[DialogPlacement.Default]: css`
inset: var(--dialog-inset);
@@ -265,11 +261,9 @@ Styled.Container = styled(Content)<{ placement: DialogPlacement; $withAnimation?
padding-bottom: var(--dialog-radius); */
}
${$withAnimation &&
css`
@media (prefers-reduced-motion: no-preference) {
&[data-state='open'] {
animation: ${keyframes`
@media (prefers-reduced-motion: no-preference) {
&[data-state='open'] {
animation: ${keyframes`
from {
opacity: 0;
}
@@ -277,19 +271,18 @@ Styled.Container = styled(Content)<{ placement: DialogPlacement; $withAnimation?
max-height: 0;
}
`} 0.15s var(--ease-out-expo);
}
}
&[data-state='closed'] {
animation: ${keyframes`
&[data-state='closed'] {
animation: ${keyframes`
to {
opacity: 0;
scale: 0.9;
max-height: 0;
}
`} 0.15s;
}
}
`}
}
`,
[DialogPlacement.Sidebar]: css`
--dialog-width: var(--sidebar-width);
@@ -299,52 +292,50 @@ Styled.Container = styled(Content)<{ placement: DialogPlacement; $withAnimation?
margin-left: auto;
}
${$withAnimation &&
css`
@media (prefers-reduced-motion: no-preference) {
&[data-state='open'] {
animation: ${keyframes`
@media (prefers-reduced-motion: no-preference) {
&[data-state='open'] {
animation: ${keyframes`
from {
translate: 100% 0;
opacity: 0;
}
`} 0.15s var(--ease-out-expo);
}
}
&[data-state='closed'] {
animation: ${keyframes`
&[data-state='closed'] {
animation: ${keyframes`
to {
translate: 100% 0;
opacity: 0;
}
`} 0.15s var(--ease-out-expo);
}
}
`}
}
`,
[DialogPlacement.Inline]: css`
${$withAnimation &&
css`
@media (prefers-reduced-motion: no-preference) {
&[data-state='open'] {
animation: ${keyframes`
@media (prefers-reduced-motion: no-preference) {
&[data-state='open'] {
animation: ${keyframes`
from {
scale: 0.99;
opacity: 0;
/* filter: blur(2px); */
/* backdrop-filter: none; */
}
`} 0.15s var(--ease-out-expo);
}
}
&[data-state='closed'] {
animation: ${keyframes`
&[data-state='closed'] {
animation: ${keyframes`
to {
scale: 0.99;
opacity: 0;
/* filter: blur(2px); */
/* backdrop-filter: none; */
}
`} 0.15s var(--ease-out-expo);
}
}
`}
}
`,
[DialogPlacement.FullScreen]: css`
--dialog-width: 100vw;
@@ -434,7 +425,7 @@ Styled.Close = styled(Close)`
}
&:hover,
&:focus-visible {
&:focus {
color: var(--color-text-2);
}
+1 -1
View File
@@ -14,7 +14,7 @@ export const DiffArrowStory: Story<DiffArrowProps> = (args) => (
DiffArrowStory.argTypes = {
direction: {
options: ['left', 'right', 'up', 'down'],
options: ['left', 'right'],
control: { type: 'select' },
defaultValue: 'right',
},
+1 -7
View File
@@ -10,7 +10,7 @@ type ElementProps = {
type StyleProps = {
className?: string;
direction?: 'right' | 'left' | 'up' | 'down';
direction?: 'right' | 'left';
};
export type DiffArrowProps = ElementProps & StyleProps;
@@ -57,11 +57,5 @@ Styled.DiffArrowContainer = styled.span<DiffArrowProps>`
left: css`
transform: scaleX(-1);
`,
up: css`
transform: rotate(-90deg);
`,
down: css`
transform: rotate(90deg);
`,
}[direction || 'right'])}
`;
+1 -1
View File
@@ -75,7 +75,7 @@ Styled.DiffValue = styled.div<{ hasInvalidNewValue?: boolean }>`
${({ hasInvalidNewValue }) =>
hasInvalidNewValue &&
css`
color: var(--color-error);
color: var(--color-negative);
`}
`;
+4 -6
View File
@@ -33,11 +33,9 @@ export const DropdownHeaderMenu = <MenuItemValue extends string>({
return (
<Root>
<Styled.Trigger className={className} asChild>
<div>
{children}
<Styled.DropdownIconButton iconName={IconName.Caret} isToggle />
</div>
<Styled.Trigger className={className}>
{children}
<Styled.DropdownIconButton iconName={IconName.Caret} isToggle />
</Styled.Trigger>
<Portal>
<Styled.Content
@@ -87,7 +85,7 @@ Styled.Trigger = styled(Trigger)`
outline: none;
:hover {
filter: brightness(var(--hover-filter-base));
filter: brightness(1.1);
}
`;
+7 -12
View File
@@ -6,34 +6,29 @@ import { StoryWrapper } from '.ladle/components';
export const DropdownMenuStory: Story<Parameters<typeof DropdownMenu>> = (args) => {
const exampleItems = [
{
value: '0',
label: 'Item 0',
onSelect: () => alert('Item 0 action'),
},
{
value: '1',
label: 'Item 1 (accent)',
label: 'Item 1',
onSelect: () => alert('Item 1 action'),
highlightColor: 'accent',
},
{
value: '2',
label: 'Item 2 (create)',
label: 'Item 2',
onSelect: () => alert('Item 2 action'),
highlightColor: 'create',
},
{
value: '3',
label: 'Item 3 (destroy)',
label: 'Item 3',
onSelect: () => alert('Item 3 action'),
highlightColor: 'destroy',
},
];
return (
<StoryWrapper>
<DropdownMenu {...args} items={exampleItems}>
<DropdownMenu
{...args}
items={exampleItems}
>
<span>Menu</span>
</DropdownMenu>
</StoryWrapper>
+6 -6
View File
@@ -13,7 +13,7 @@ export type DropdownMenuItem<T> = {
label: React.ReactNode;
onSelect?: () => void;
separator?: boolean;
highlightColor?: 'accent' | 'create' | 'destroy';
highlightColor?: 'accent' | 'positive' | 'negative';
};
type StyleProps = {
@@ -82,7 +82,7 @@ Styled.Separator = styled(Separator)`
margin: 0.25rem 1rem;
`;
Styled.Item = styled(Item)<{ $highlightColor: 'accent' | 'create' | 'destroy' }>`
Styled.Item = styled(Item)<{ $highlightColor: 'accent' | 'positive' | 'negative' }>`
${popoverMixins.item}
--item-font-size: var(--dropdownMenu-item-font-size);
${({ $highlightColor }) =>
@@ -90,11 +90,11 @@ Styled.Item = styled(Item)<{ $highlightColor: 'accent' | 'create' | 'destroy' }>
['accent']: `
--item-highlighted-textColor: var(--color-accent);
`,
['create']: `
--item-highlighted-textColor: var(--color-green);
['positive']: `
--item-highlighted-textColor: var(--color-positive);
`,
['destroy']: `
--item-highlighted-textColor: var(--color-red);
['negative']: `
--item-highlighted-textColor: var(--color-negative);
`,
}[$highlightColor])}
+1 -4
View File
@@ -9,7 +9,6 @@ import {
RadioItem,
Portal,
} from '@radix-ui/react-dropdown-menu';
import { CheckIcon } from '@radix-ui/react-icons';
import { type MenuItem } from '@/constants/menus';
@@ -91,9 +90,7 @@ export const DropdownSelectMenu = <MenuItemValue extends string>({
{slotAfter}
<Styled.ItemIndicator>
<CheckIcon />
</Styled.ItemIndicator>
<Styled.ItemIndicator>{/* <CheckIcon /> */}</Styled.ItemIndicator>
</Styled.RadioItem>
))}
</RadioGroup>
+3 -3
View File
@@ -32,7 +32,7 @@ export const FormInput = forwardRef<HTMLInputElement, FormInputProps>(
isValidationAttached={validationConfig?.attached}
>
<Styled.InputContainer hasSlotRight={!!slotRight}>
<Styled.WithLabel label={label} inputID={id} disabled={otherProps?.disabled}>
<Styled.WithLabel label={label} inputID={id}>
<Input ref={ref} id={id} {...otherProps} />
</Styled.WithLabel>
{slotRight}
@@ -85,11 +85,11 @@ Styled.InputContainer = styled.div<{ hasSlotRight?: boolean }>`
`}
`;
Styled.WithLabel = styled(WithLabel)<{ disabled?: boolean }>`
Styled.WithLabel = styled(WithLabel)`
${formMixins.inputLabel}
label {
${({ disabled }) => !disabled && 'cursor: text;'}
cursor: text;
padding: var(--form-input-paddingY) var(--form-input-paddingX) 0;
}
`;
+29 -2
View File
@@ -1,16 +1,43 @@
import styled, { AnyStyledComponent } from 'styled-components';
import { layoutMixins } from '@/styles/layoutMixins';
import { Icon, IconName } from '@/components/Icon';
export const GreenCheckCircle = ({ className }: { className?: string }) => (
<Styled.Icon className={className} iconName={IconName.CheckCircle} />
<Styled.GreenCheckCircle className={className}>
<Icon iconName={IconName.Check} />
</Styled.GreenCheckCircle>
);
const Styled: Record<string, AnyStyledComponent> = {};
Styled.Icon = styled(Icon)`
Styled.GreenCheckCircle = styled.div`
${layoutMixins.stack}
--icon-size: 1.25rem;
--icon-border-width: 3px;
width: var(--icon-size);
height: var(--icon-size);
border-radius: 50%;
align-items: center;
&:before {
content: '';
width: calc(var(--icon-size) - var(--icon-border-width) * 2);
height: calc(var(--icon-size) - var(--icon-border-width) * 2);
border: solid var(--icon-border-width) var(--color-positive);
border-radius: 50%;
opacity: 0.6;
}
svg {
width: calc(var(--icon-size) / 3);
height: calc(var(--icon-size) / 3);
color: var(--color-positive);
justify-self: center;
}
`;
+5 -45
View File
@@ -6,17 +6,12 @@ import {
AddressConnectorIcon,
ArrowIcon,
Bar3Icon,
BellIcon,
BellStrokeIcon,
BoxCloseIcon,
CalculatorIcon,
CaretIcon,
CautionCircleStrokeIcon,
CautionCircleIcon,
ChaosLabsIcon,
ChatIcon,
CheckIcon,
CheckCircleIcon,
ChevronLeftIcon,
ChevronRightIcon,
ClockIcon,
@@ -25,10 +20,10 @@ import {
CoinsIcon,
CommentIcon,
CopyIcon,
DepositIcon,
DepthChartIcon,
CubeIcon,
DiscordIcon,
EtherscanIcon,
DepthChartIcon,
ExportKeysIcon,
FeedbackIcon,
FileIcon,
@@ -38,14 +33,11 @@ import {
HelpCircleIcon,
HideIcon,
HistoryIcon,
LeaderboardIcon,
LinkOutIcon,
LockIcon,
LogoShortIcon,
MenuIcon,
MigrateIcon,
MintscanIcon,
MoonIcon,
OrderbookIcon,
OrderCanceledIcon,
OrderFilledIcon,
@@ -55,20 +47,16 @@ import {
OrderUntriggeredIcon,
OverviewIcon,
PencilIcon,
PlayIcon,
PlusIcon,
PositionsIcon,
PriceChartIcon,
PrivacyIcon,
QrIcon,
RewardStarIcon,
SearchIcon,
SendIcon,
ShareIcon,
ShowIcon,
TogglesMenuIcon,
StarIcon,
SunIcon,
TerminalIcon,
TradeIcon,
TransferIcon,
@@ -77,24 +65,18 @@ import {
WarningIcon,
WebsiteIcon,
WhitepaperIcon,
WithdrawIcon,
} from '@/icons';
export enum IconName {
AddressConnector = 'AddressConnector',
Arrow = 'Arrow',
Bar3 = 'Bar3',
Bell = 'Bell',
BellStroked = 'BellStroked',
BoxClose = 'BoxClose',
Calculator = 'Calculator',
Caret = 'Caret',
CautionCircle = 'CautionCircle',
CautionCircleStroked = 'CautionCircleStroked',
ChaosLabs = 'ChaosLabs',
Chat = 'Chat',
Check = 'Check',
CheckCircle = 'CheckCircle',
ChevronLeft = 'ChevronLeft',
ChevronRight = 'ChevronRight',
Clock = 'Clock',
@@ -103,7 +85,7 @@ export enum IconName {
Coins = 'Coins',
Comment = 'Comment',
Copy = 'Copy',
Deposit = 'Deposit',
Cube = 'Cube',
DepthChart = 'DepthChart',
Discord = 'Discord',
Etherscan = 'Etherscan',
@@ -116,14 +98,11 @@ export enum IconName {
HelpCircle = 'HelpCircle',
Hide = 'Hide',
History = 'History',
Leaderboard = 'Leaderboard',
LinkOut = 'LinkOut',
Lock = 'Lock',
LogoShort = 'LogoShort',
Menu = 'Menu',
Migrate = 'Migrate',
Mintscan = 'Mintscan',
Moon = 'Moon',
Onboarding = 'Onboarding',
Orderbook = 'OrderbookIcon',
OrderCanceled = 'OrderCanceled',
@@ -134,19 +113,15 @@ export enum IconName {
OrderUntriggered = 'OrderUntriggered',
Overview = 'Overview',
Pencil = 'Pencil',
Play = 'Play',
Plus = 'Plus',
Positions = 'Positions',
PriceChart = 'PriceChart',
Privacy = 'Privacy',
Qr = 'Qr',
RewardStar = 'RewardStar',
Search = 'Search',
Send = 'Send',
Share = 'Share',
Show = 'Show',
Star = 'Star',
Sun = 'Sun',
Terminal = 'Terminal',
TogglesMenu = 'TogglesMenu',
Trade = 'Trade',
@@ -156,24 +131,18 @@ export enum IconName {
Warning = 'Warning',
Website = 'Website',
Whitepaper = 'Whitepaper',
Withdraw = 'Withdraw',
}
const icons = {
[IconName.AddressConnector]: AddressConnectorIcon,
[IconName.Arrow]: ArrowIcon,
[IconName.Bar3]: Bar3Icon,
[IconName.Bell]: BellIcon,
[IconName.BellStroked]: BellStrokeIcon,
[IconName.BoxClose]: BoxCloseIcon,
[IconName.Calculator]: CalculatorIcon,
[IconName.Caret]: CaretIcon,
[IconName.CautionCircle]: CautionCircleIcon,
[IconName.CautionCircleStroked]: CautionCircleStrokeIcon,
[IconName.ChaosLabs]: ChaosLabsIcon,
[IconName.Chat]: ChatIcon,
[IconName.Check]: CheckIcon,
[IconName.CheckCircle]: CheckCircleIcon,
[IconName.ChevronLeft]: ChevronLeftIcon,
[IconName.ChevronRight]: ChevronRightIcon,
[IconName.Clock]: ClockIcon,
@@ -182,7 +151,7 @@ const icons = {
[IconName.Coins]: CoinsIcon,
[IconName.Comment]: CommentIcon,
[IconName.Copy]: CopyIcon,
[IconName.Deposit]: DepositIcon,
[IconName.Cube]: CubeIcon,
[IconName.DepthChart]: DepthChartIcon,
[IconName.Discord]: DiscordIcon,
[IconName.Etherscan]: EtherscanIcon,
@@ -195,14 +164,11 @@ const icons = {
[IconName.HelpCircle]: HelpCircleIcon,
[IconName.Hide]: HideIcon,
[IconName.History]: HistoryIcon,
[IconName.Leaderboard]: LeaderboardIcon,
[IconName.LinkOut]: LinkOutIcon,
[IconName.Lock]: LockIcon,
[IconName.LogoShort]: LogoShortIcon,
[IconName.Menu]: MenuIcon,
[IconName.Migrate]: MigrateIcon,
[IconName.Mintscan]: MintscanIcon,
[IconName.Moon]: MoonIcon,
[IconName.Orderbook]: OrderbookIcon,
[IconName.OrderCanceled]: OrderCanceledIcon,
[IconName.OrderFilled]: OrderFilledIcon,
@@ -212,19 +178,15 @@ const icons = {
[IconName.OrderUntriggered]: OrderUntriggeredIcon,
[IconName.Overview]: OverviewIcon,
[IconName.Pencil]: PencilIcon,
[IconName.Play]: PlayIcon,
[IconName.Plus]: PlusIcon,
[IconName.Positions]: PositionsIcon,
[IconName.PriceChart]: PriceChartIcon,
[IconName.Privacy]: PrivacyIcon,
[IconName.Qr]: QrIcon,
[IconName.RewardStar]: RewardStarIcon,
[IconName.Search]: SearchIcon,
[IconName.Send]: SendIcon,
[IconName.Share]: ShareIcon,
[IconName.Show]: ShowIcon,
[IconName.Star]: StarIcon,
[IconName.Sun]: SunIcon,
[IconName.Terminal]: TerminalIcon,
[IconName.TogglesMenu]: TogglesMenuIcon,
[IconName.Trade]: TradeIcon,
@@ -234,7 +196,6 @@ const icons = {
[IconName.Warning]: WarningIcon,
[IconName.Website]: WebsiteIcon,
[IconName.Whitepaper]: WhitepaperIcon,
[IconName.Withdraw]: WithdrawIcon,
} as Record<IconName, ElementType>;
type ElementProps = {
@@ -252,8 +213,7 @@ export const Icon = styled(
iconComponent: Component = iconName && icons[iconName],
className,
...props
}: ElementProps & StyleProps) =>
Component ? <Component className={className} {...props} /> : null
}: ElementProps & StyleProps) => (Component ? <Component className={className} {...props} /> : null)
)`
width: 1em;
height: 1em;
-1
View File
@@ -67,7 +67,6 @@ const Styled: Record<string, AnyStyledComponent> = {};
const buttonMixin = css`
// Params
--button-icon-size: 1.125em;
--button-padding: 0;
// Rules
> * {
+21 -40
View File
@@ -10,7 +10,7 @@ import {
USD_DECIMALS,
} from '@/constants/numbers';
import { BIG_NUMBERS } from '@/lib/numbers';
import { BIG_NUMBERS, MustBigNumber } from '@/lib/numbers';
import { useLocaleSeparators } from '@/hooks';
export enum InputType {
@@ -29,38 +29,31 @@ type StyleProps = {
type ElementProps = {
type?: InputType;
value?: string | number | null;
allowNegative?: boolean;
decimals?: number;
disabled?: boolean;
id?: string;
max?: number;
onBlur?: () => void;
onChange?:
| Dispatch<SetStateAction<string>>
| React.ReactEventHandler<HTMLInputElement>
| ((values: NumberFormatValues, e: SourceInfo) => void);
onFocus?: () => void;
onInput?: ({
value,
floatValue,
formattedValue,
}: {
value: string;
floatValue?: number;
formattedValue: string;
}) => void;
placeholder?: string;
};
type ConditionalProps =
| {
allowNegative?: boolean;
decimals?: number;
max?: number;
onChange?: (values: NumberFormatValues, e: SourceInfo) => void;
onInput?: ({
value,
floatValue,
formattedValue,
}: {
value: string;
floatValue?: number;
formattedValue: string;
}) => void;
}
| {
allowNegative?: never;
decimals?: never;
max?: never;
onChange?: Dispatch<SetStateAction<string>> | React.ReactEventHandler<HTMLInputElement>;
onInput?: (e: SyntheticInputEvent) => void;
};
export type InputProps = ElementProps & StyleProps & ConditionalProps;
export type InputProps = ElementProps & StyleProps;
export const Input = forwardRef<HTMLInputElement, InputProps>(
(
@@ -136,7 +129,6 @@ export const Input = forwardRef<HTMLInputElement, InputProps>(
placeholder={placeholder}
value={value}
// Other
data-1p-ignore // prevent 1Password fill
{...otherProps}
/>
) : (
@@ -145,7 +137,6 @@ export const Input = forwardRef<HTMLInputElement, InputProps>(
ref={ref as React.Ref<typeof NumericFormat<unknown>>}
id={id}
// NumericFormat
valueIsNumericString
allowNegative={allowNegative}
decimalScale={decimals}
decimalSeparator={LOCALE_DECIMAL_SEPARATOR}
@@ -169,7 +160,7 @@ export const Input = forwardRef<HTMLInputElement, InputProps>(
? undefined
: Number(formattedValue.replace(',', '.'));
onInput?.({ value, floatValue, formattedValue, ...e });
onInput?.({ value, floatValue, formattedValue });
}}
// Native
disabled={disabled}
@@ -177,7 +168,6 @@ export const Input = forwardRef<HTMLInputElement, InputProps>(
value={formattedValue}
autoComplete="off"
autoCorrect="off"
data-1p-ignore // prevent 1Password fill
{...otherProps}
/>
)}
@@ -198,6 +188,7 @@ Styled.InputContainer = styled.div`
border-radius: inherit;
input {
user-select: all;
flex: 1;
width: 100%;
}
@@ -216,16 +207,6 @@ const InputStyle = css`
color: var(--color-text-0);
opacity: 1;
}
// Input autofill Styles
&:-webkit-autofill,
&:-webkit-autofill:focus {
transition: background-color 600000s 0s, color 600000s 0s;
}
&[data-autocompleted] {
background-color: var(--input-backgroundColor) !important;
}
`;
Styled.NumericFormat = styled(NumericFormat)`
+3 -3
View File
@@ -12,7 +12,7 @@ type ElementProps = {
};
children: React.ReactNode;
href?: string;
onClick?: (e: MouseEvent) => void;
onClick?: () => void;
withIcon?: boolean;
};
@@ -37,12 +37,12 @@ export const Link = forwardRef<HTMLAnchorElement, ElementProps & StyleProps>(
ref={ref}
className={className}
href={href}
onClick={(e: MouseEvent) => {
onClick={() => {
if (analyticsConfig) {
console.log(analyticsConfig);
}
onClick?.(e);
onClick?.();
}}
rel="noopener noreferrer"
target="_blank"
@@ -11,7 +11,6 @@ export const LoadingSpinner: React.FC<{
return (
<Styled.Spinner className={className}>
<Styled.LoadingSpinnerSvg
id={id}
width="38"
height="38"
viewBox="0 0 38 38"
+1 -1
View File
@@ -1,11 +1,11 @@
import styled, { type AnyStyledComponent } from 'styled-components';
import { RiskLevels } from '@/constants/abacus';
import { UsageColorFromRiskLevel } from '@/styles/colors';
import { Ring } from '@/components/Ring';
import { abacusHelper } from '@/lib/abacus';
import { UsageColorFromRiskLevel } from '@/lib/styles';
type ElementProps = {
value: number;
+65 -15
View File
@@ -1,4 +1,4 @@
import { forwardRef, Ref } from 'react';
import { forwardRef, Ref, useEffect, useRef, useState } from 'react';
import styled, { type AnyStyledComponent, css, keyframes } from 'styled-components';
import { NavLink, matchPath, useLocation } from 'react-router-dom';
@@ -21,6 +21,8 @@ import {
import { layoutMixins } from '@/styles/layoutMixins';
import { isTruthy } from '@/lib/isTruthy';
import { Tag } from './Tag';
import { Icon, IconName } from './Icon';
@@ -59,6 +61,7 @@ const NavItem = forwardRef(
<>
{slotBefore}
<span>
{/* {`${label}${subitems?.length ? ' ' : ''}`} */}
{label}
{tag && (
<>
@@ -68,7 +71,7 @@ const NavItem = forwardRef(
)}
</span>
{slotAfter}
{subitems?.length && <Styled.Icon iconName={IconName.Triangle} />}
{subitems?.length && <Styled.Icon iconName={IconName.Caret} />}
</>
);
@@ -97,6 +100,8 @@ const NavItem = forwardRef(
}
);
type TriggerRef = HTMLAnchorElement | HTMLDivElement | HTMLButtonElement | null;
export const NavigationMenu = <MenuItemValue extends string, MenuGroupValue extends string>({
onSelectItem,
onSelectGroup,
@@ -107,6 +112,33 @@ export const NavigationMenu = <MenuItemValue extends string, MenuGroupValue exte
dir = 'ltr',
className,
}: ElementProps<MenuItemValue, MenuGroupValue> & StyleProps) => {
// Disable click (close) in the first 500ms after hover (open)
// https://github.com/radix-ui/primitives/issues/1630#issuecomment-1545995075
const [clickIsDisabled, setClickIsDisabled] = useState(false);
const triggerRefs = useRef({} as { [value: string]: TriggerRef });
useEffect(() => {
const observer = new MutationObserver((mutationsList) => {
for (const mutation of mutationsList) {
if (
mutation.type === 'attributes' &&
mutation.attributeName === 'data-state' &&
(mutation.target as unknown as HTMLOrSVGElement).dataset.state === 'open' &&
mutation.target !== document.activeElement
) {
setClickIsDisabled(true);
setTimeout(() => setClickIsDisabled(false), 500);
}
}
});
for (const element of Object.values(triggerRefs.current).filter(isTruthy)) {
observer.observe(element, { attributes: true });
}
return () => observer.disconnect();
}, []);
const renderSubitems = ({
item,
depth = 0,
@@ -116,18 +148,18 @@ export const NavigationMenu = <MenuItemValue extends string, MenuGroupValue exte
}) => (
<>
<Styled.SubMenuTrigger
asChild
onPointerMove={(e: MouseEvent) => e.preventDefault()}
onPointerLeave={(e: MouseEvent) => e.preventDefault()}
asChild={depth > 0}
ref={(ref: TriggerRef) => (triggerRefs.current[item.value] = ref)}
onClick={(e: MouseEvent) => {
if (clickIsDisabled) {
e.preventDefault();
}
}}
>
<Styled.NavItem onSelect={onSelectGroup} orientation={itemOrientation} {...item} />
</Styled.SubMenuTrigger>
<Styled.Content
onPointerEnter={(e: MouseEvent) => e.preventDefault()}
onPointerLeave={(e: MouseEvent) => e.preventDefault()}
data-placement={submenuPlacement}
>
<Styled.Content data-placement={submenuPlacement}>
<Styled.Sub data-placement={submenuPlacement}>
<Styled.List
data-orientation={depth > 0 ? 'menu' : orientation === 'vertical' ? 'vertical' : 'menu'}
@@ -378,12 +410,25 @@ Styled.List = styled(List)`
`;
Styled.ListItem = styled(Item)`
/* display: contents; */
display: grid;
position: relative;
${Styled.List}[data-orientation="horizontal"] > & {
gap: var(--submenu-side-offset);
}
/* ${Styled.List}[data-orientation="menu"] > & {
grid-template-columns: 1fr 0;
align-items: start;
gap: 2rem;
} */
/* &:has([data-state="open"]) {
position: sticky;
left: 0;
right: 0;
} */
`;
Styled.SubMenuTrigger = styled(Trigger)`
@@ -391,6 +436,11 @@ Styled.SubMenuTrigger = styled(Trigger)`
outline-offset: -2px;
&[data-state='open'] {
div {
background-color: var(--navigationMenu-item-checked-backgroundColor);
color: var(--navigationMenu-item-checked-textColor);
}
svg {
rotate: 0.5turn;
}
@@ -402,10 +452,11 @@ Styled.NavItem = styled(NavItem)<{ orientation: 'horizontal' | 'vertical' }>`
subitems?.length
? css`
${popoverMixins.trigger}
--trigger-open-backgroundColor: var(--navigationMenu-item-checked-backgroundColor);
--trigger-backgroundColor: transparent;
--trigger-open-backgroundColor: var(--color-layer-3);
`
: css`
&:hover:not(:active) {
&:hover:not(.active) {
background-color: var(--navigationMenu-item-highlighted-backgroundColor);
color: var(--navigationMenu-item-highlighted-textColor);
}
@@ -419,6 +470,8 @@ Styled.NavItem = styled(NavItem)<{ orientation: 'horizontal' | 'vertical' }>`
--item-radius: var(--navigationMenu-item-radius);
--item-padding: var(--navigationMenu-item-padding);
/* ${popoverMixins.backdropOverlay} */
${layoutMixins.scrollSnapItem}
min-height: var(--navigationMenu-item-height);
@@ -472,9 +525,6 @@ Styled.NavItem = styled(NavItem)<{ orientation: 'horizontal' | 'vertical' }>`
`;
Styled.Icon = styled(Icon)`
font-size: 0.375em;
transition: rotate 0.3s var(--ease-out-expo);
${Styled.List}[data-orientation="menu"] & {
rotate: -0.25turn;
}
-201
View File
@@ -1,201 +0,0 @@
import React, { type MouseEvent } from 'react';
import styled, { css } from 'styled-components';
import { ButtonShape, ButtonSize } from '@/constants/buttons';
import {
NotificationStatus,
type Notification as NotificationDataType,
} from '@/constants/notifications';
import { useNotifications } from '@/hooks/useNotifications';
import { popoverMixins } from '@/styles/popoverMixins';
import { layoutMixins } from '@/styles/layoutMixins';
import { IconButton } from '@/components/IconButton';
import { IconName } from '@/components/Icon';
import { Output, OutputType } from '@/components/Output';
type ElementProps = {
notification: NotificationDataType;
onClick?: () => void;
slotIcon?: React.ReactNode;
slotTitle?: React.ReactNode;
slotTitleLeft?: React.ReactNode;
slotTitleRight?: React.ReactNode;
slotDescription?: React.ReactNode;
slotCustomContent?: React.ReactNode;
slotAction?: React.ReactNode;
withClose?: boolean;
};
type StyleProps = {
className?: string;
isToast?: boolean;
};
export type NotificationProps = ElementProps & StyleProps;
export const Notification = ({
className,
isToast,
notification,
onClick,
slotIcon,
slotTitle,
slotTitleLeft,
slotTitleRight,
slotDescription,
slotCustomContent,
slotAction,
withClose = !isToast,
}: NotificationProps) => {
const { markCleared, markSeen } = useNotifications();
return (
<$Container className={className} isToast={isToast} onClick={onClick}>
<$Header>
{slotTitleLeft ?? (slotIcon && <$Icon>{slotIcon}</$Icon>)}
<$Title>{slotTitle}</$Title>
{slotTitleRight}
{withClose && !isToast && (
<$ActionItems>
<$Output
type={OutputType.RelativeTime}
value={notification.timestamps[NotificationStatus.Triggered]}
/>
{notification.status < NotificationStatus.Seen ? <$UnreadIndicator /> : null}
{notification.status < NotificationStatus.Cleared ? (
<$IconButton
iconName={IconName.Close}
shape={ButtonShape.Square}
size={ButtonSize.XSmall}
onClick={(e: MouseEvent) => {
e.stopPropagation();
if (notification.status < NotificationStatus.Seen) {
markSeen(notification);
} else if (notification.status < NotificationStatus.Cleared) {
markCleared(notification);
}
}}
/>
) : null}
</$ActionItems>
)}
</$Header>
<$Description>{slotCustomContent ?? slotDescription}</$Description>
{slotAction && <$Action>{slotAction}</$Action>}
</$Container>
);
};
const $Container = styled.div<{ isToast?: boolean }>`
// Params
--toast-icon-size: 1.75em;
// Rules
${popoverMixins.popover}
overflow: visible;
padding: 1rem 1.25rem;
${({ isToast }) =>
isToast
? css`
padding: 1rem;
box-shadow: 0 0 0 var(--border-width) var(--color-border),
// border
0 0 0.5rem 0.1rem var(--color-layer-2); // shadow
`
: css`
flex: 1;
background-color: transparent;
border-radius: 0;
backdrop-filter: none;
`}
`;
const $Header = styled.header`
${layoutMixins.row}
position: relative;
`;
const $Icon = styled.div`
${layoutMixins.row}
float: left;
margin-right: 0.5rem;
line-height: 1;
> svg,
img {
width: 1.5rem;
height: 1.5rem;
}
`;
const $Title = styled.div`
flex: 1;
font: var(--font-base-medium);
color: var(--color-text-2);
overflow: hidden;
text-overflow: ellipsis;
`;
const $Description = styled.div`
margin-top: 0.5rem;
color: var(--color-text-0);
font: var(--font-small-book);
`;
const $Action = styled.div`
margin-top: 0.5rem;
font: var(--font-small-book);
`;
const $ActionItems = styled.div`
min-width: fit-content;
padding-left: 0.5rem;
height: 100%;
${layoutMixins.row};
justify-content: end;
gap: 0.5rem;
position: absolute;
right: 0;
top: 0;
background-color: var(--color-layer-2);
font: var(--font-mini-book);
display: none;
z-index: 1;
${$Container}:hover & {
display: flex;
}
`;
const $UnreadIndicator = styled.div`
width: 0.5rem;
height: 0.5rem;
border-radius: 50%;
background-color: var(--color-accent);
border: 1px solid var(--color-layer-2);
`;
const $Output = styled(Output)`
color: var(--color-text-0);
`;
const $IconButton = styled(IconButton)`
--button-border: none;
--button-textColor: var(--color-text-0);
--button-hover-textColor: var(--color-text-1);
`;
+1 -11
View File
@@ -65,9 +65,6 @@ type ElementProps = {
resolution?: number;
stripRelativeWords?: boolean;
};
timeOptions?: {
useUTC?: boolean;
};
tag?: React.ReactNode;
withParentheses?: boolean;
locale?: string;
@@ -92,7 +89,6 @@ export const Output = ({
relativeTimeFormatOptions = {
format: 'singleCharacter',
},
timeOptions,
tag,
withParentheses,
locale = navigator.language || 'en-US',
@@ -120,7 +116,6 @@ export const Output = ({
{value?.toString() ?? null}
{tag && <Tag>{tag}</Tag>}
{slotRight}
</Styled.Text>
);
}
@@ -170,21 +165,16 @@ export const Output = ({
if ((typeof value !== 'string' && typeof value !== 'number') || !value) return null;
const date = new Date(value);
const dateString = {
[OutputType.Date]: date.toLocaleString(selectedLocale, {
dateStyle: 'medium',
timeZone: timeOptions?.useUTC ? 'UTC' : undefined,
}),
[OutputType.Date]: date.toLocaleString(selectedLocale, { dateStyle: 'medium' }),
[OutputType.DateTime]: date.toLocaleString(selectedLocale, {
dateStyle: 'short',
timeStyle: 'short',
timeZone: timeOptions?.useUTC ? 'UTC' : undefined,
}),
[OutputType.Time]: date.toLocaleString(selectedLocale, {
hour12: false,
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
timeZone: timeOptions?.useUTC ? 'UTC' : undefined,
}),
}[type];
+3 -5
View File
@@ -1,10 +1,10 @@
import type { Story } from '@ladle/react';
import { Panel, PanelProps } from '@/components/Panel';
import { Panel } from '@/components/Panel';
import { StoryWrapper } from '.ladle/components';
export const PanelStory: Story<PanelProps> = (args) => {
export const PanelStory: Story<{ slotHeader: React.ReactNode, children?: React.ReactNode }> = (args) => {
return (
<StoryWrapper>
<Panel {...args} />
@@ -13,8 +13,6 @@ export const PanelStory: Story<PanelProps> = (args) => {
};
PanelStory.args = {
slotHeaderContent: 'Header',
slotHeader: 'Header',
children: 'Content',
slotRight: '1️⃣',
hasSeparator: true,
};
+31 -67
View File
@@ -4,116 +4,79 @@ import { Link } from 'react-router-dom';
import { Icon, IconName } from '@/components/Icon';
import { layoutMixins } from '@/styles/layoutMixins';
import { breakpoints } from '@/styles';
type ElementProps = {
slotHeaderContent?: React.ReactNode;
type PanelProps = {
slotHeaderContent?: string;
slotHeader?: React.ReactNode;
slotRight?: React.ReactNode;
children?: React.ReactNode;
href?: string;
onHeaderClick?: () => void;
onClick?: () => void;
};
type StyleProps = {
type PanelStyleProps = {
className?: string;
hasSeparator?: boolean;
};
export type PanelProps = ElementProps & StyleProps;
export const Panel = ({
slotHeaderContent,
slotHeader,
slotRight,
children,
href,
onHeaderClick,
onClick,
hasSeparator,
className,
}: PanelProps) => (
<Styled.Panel onClick={onClick} className={className}>
<Styled.Left>
{href ? (
<Link to={href}>
{slotHeader ? (
slotHeader
) : (
<Styled.Header role="button" onClick={onHeaderClick} hasSeparator={hasSeparator}>
{slotHeaderContent}
<Styled.Icon iconName={IconName.ChevronRight} />
</Styled.Header>
)}
</Link>
) : slotHeader ? (
slotHeader
) : (
slotHeaderContent && (
}: PanelProps & PanelStyleProps) => (
<Styled.Panel>
{href ? (
<Link to={href}>
{slotHeader ? (
slotHeader
) : (
<Styled.Header role="button" onClick={onHeaderClick} hasSeparator={hasSeparator}>
{slotHeaderContent}
<Styled.Icon iconName={IconName.ChevronRight} />
</Styled.Header>
)
)}
<Styled.Content>{children}</Styled.Content>
</Styled.Left>
{slotRight}
)}
</Link>
) : slotHeader ? (
slotHeader
) : (
slotHeaderContent && (
<Styled.Header role="button" onClick={onHeaderClick} hasSeparator={hasSeparator}>
{slotHeaderContent}
</Styled.Header>
)
)}
<Styled.Content className={className}>{children}</Styled.Content>
</Styled.Panel>
);
const Styled: Record<string, AnyStyledComponent> = {};
Styled.Panel = styled.section<{ onClick?: () => void }>`
--panel-paddingY: 1rem;
--panel-paddingX: 1rem;
--panel-content-paddingY: var(--panel-paddingY);
--panel-content-paddingX: var(--panel-paddingX);
@media ${breakpoints.notTablet} {
--panel-paddingX: 1.5rem;
--panel-paddingY: 1.25rem;
--panel-content-paddingY: 1rem;
}
${layoutMixins.row}
Styled.Panel = styled.section`
${layoutMixins.flexColumn}
background-color: var(--color-layer-3);
border-radius: 0.875rem;
${({ onClick }) =>
onClick &&
css`
cursor: pointer;
&:hover {
button:not(:disabled) {
color: var(--button-hover-textColor);
filter: var(--button-hover-filter);
}
}
`}
`;
Styled.Left = styled.div`
${layoutMixins.flexColumn}
width: 100%;
`;
Styled.Header = styled.header<{ hasSeparator?: boolean }>`
${layoutMixins.spacedRow}
padding: var(--panel-paddingY) var(--panel-paddingX);
padding: 0.875rem 1rem 0.625rem;
font-size: 0.875rem;
${({ hasSeparator }) =>
hasSeparator &&
css`
padding-bottom: 0.625rem;
box-shadow: 0 var(--border-width) var(--border-color);
`}
`;
Styled.Icon = styled(Icon)`
color: var(--color-text-0);
font-size: 0.625rem;
`;
@@ -121,5 +84,6 @@ Styled.Content = styled.div`
${layoutMixins.scrollArea}
${layoutMixins.stickyArea0}
--stickyArea0-background: transparent;
padding: var(--panel-content-paddingY) var(--panel-content-paddingX);
padding: 0.5rem 1rem;
`;
+2 -3
View File
@@ -11,7 +11,6 @@ type ElementProps = {
};
type StyleProps = {
className?: string;
hasLogo?: boolean;
size?: number;
};
@@ -19,7 +18,7 @@ type StyleProps = {
const DARK_LOGO_MARK_URL = '/logos/logo-mark-dark.svg';
const LIGHT_LOGO_MARK_URL = '/logos/logo-mark-light.svg';
export const QrCode = ({ className, value, hasLogo, size = 300 }: ElementProps & StyleProps) => {
export const QrCode = ({ value, hasLogo, size = 300 }: ElementProps & StyleProps) => {
const ref = useRef<HTMLDivElement>(null);
const appTheme: AppTheme = useSelector(getAppTheme);
@@ -75,7 +74,7 @@ export const QrCode = ({ className, value, hasLogo, size = 300 }: ElementProps &
}
}, [appTheme, hasLogo]);
return <Styled.QrCode className={className} ref={ref} />;
return <Styled.QrCode ref={ref} />;
};
const Styled: Record<string, AnyStyledComponent> = {};
+1 -7
View File
@@ -1,4 +1,4 @@
import { useEffect, useRef, useState } from 'react';
import { useState } from 'react';
import styled, { type AnyStyledComponent, css } from 'styled-components';
import { layoutMixins } from '@/styles/layoutMixins';
@@ -22,11 +22,6 @@ export const SearchInput = ({
}: SearchInputProps) => {
const [isOpen, setIsOpen] = useState(false);
const [value, setValue] = useState('');
const inputRef = useRef<HTMLInputElement | null>(null);
useEffect(() => {
if (isOpen) inputRef?.current?.focus();
}, [inputRef, isOpen]);
return (
<Styled.Search>
@@ -46,7 +41,6 @@ export const SearchInput = ({
/>
<Styled.Input
autoFocus
ref={inputRef}
value={value}
isOpen={isOpen}
type={type}
+8 -13
View File
@@ -12,8 +12,6 @@ import { WithDetailsReceipt } from '@/components/WithDetailsReceipt';
import { WithLabel } from '@/components/WithLabel';
import { layoutMixins } from '@/styles/layoutMixins';
import { formMixins } from '@/styles/formMixins';
import breakpoints from '@/styles/breakpoints';
type ElementProps = {
asChild?: boolean;
@@ -86,30 +84,27 @@ export const SearchSelectMenu = ({
const Styled: Record<string, AnyStyledComponent> = {};
Styled.SearchSelectMenu = styled.div`
${layoutMixins.column}
`;
Styled.MenuTrigger = styled.div`
height: var(--form-input-height);
${layoutMixins.spacedRow}
align-items: center;
padding: var(--form-input-paddingY) var(--form-input-paddingX);
@media ${breakpoints.tablet} {
height: var(--form-input-height-mobile);
}
padding: 0 1rem;
`;
Styled.WithLabel = styled(WithLabel)`
${formMixins.inputLabel}
--label-textColor: var(--color-text-0);
gap: 0.25rem;
label {
font: var(--font-mini-book);
}
`;
Styled.SearchSelectMenu = styled.div`
${layoutMixins.column}
`;
Styled.WithDetailsReceipt = styled(WithDetailsReceipt)`
--withReceipt-backgroundColor: var(--color-layer-2);
@@ -142,7 +137,7 @@ Styled.ComboboxMenu = styled(ComboboxMenu)`
--stickyArea1-topHeight: var(--form-input-height);
input:focus-visible {
input:focus {
outline: none;
}
+9 -15
View File
@@ -1,5 +1,5 @@
import React from 'react';
import styled, { css, type AnyStyledComponent } from 'styled-components';
import styled, { type AnyStyledComponent } from 'styled-components';
import {
Root,
@@ -13,13 +13,11 @@ import {
Portal,
Icon as SelectIcon,
} from '@radix-ui/react-select';
import { CheckIcon } from '@radix-ui/react-icons';
import { popoverMixins } from '@/styles/popoverMixins';
import { formMixins } from '@/styles/formMixins';
import { WithLabel } from '@/components/WithLabel';
import { Icon, IconName } from './Icon';
export const SelectMenu = <T extends string>({
children,
@@ -46,9 +44,7 @@ export const SelectMenu = <T extends string>({
) : (
<Value />
)}
{React.Children.toArray(children).length > 1 && (
<Styled.TriggerIcon iconName={IconName.Triangle} />
)}
{React.Children.toArray(children).length > 1 && <Styled.DropdownIcon />}
</Styled.Trigger>
<Portal>
<Styled.Content className={className}>
@@ -72,9 +68,7 @@ export const SelectItem = <T extends string>({
}) => (
<Styled.Item className={className} value={value}>
<ItemText>{label}</ItemText>
<Styled.ItemIndicator>
<CheckIcon />
</Styled.ItemIndicator>
<Styled.ItemIndicator>{/* <CheckIcon /> */}</Styled.ItemIndicator>
</Styled.Item>
);
@@ -94,6 +88,12 @@ Styled.Trigger = styled(Trigger)<{ $withBlur?: boolean }>`
}
`;
Styled.DropdownIcon = styled(SelectIcon)`
font-size: 0.675em;
color: var(--color-text-1);
margin-left: auto;
`;
Styled.Content = styled(Content)`
--select-menu-content-maxWidth: ;
max-width: var(--select-menu-content-maxWidth);
@@ -116,9 +116,3 @@ Styled.WithLabel = styled(WithLabel)`
${formMixins.inputLabel}
border-radius: 0;
`;
Styled.TriggerIcon = styled(Icon)`
width: 0.625rem;
height: 0.375rem;
color: var(--color-text-0);
`;

Some files were not shown because too many files have changed in this diff Show More