diff --git a/.github/workflows/cleanup.yml b/.github/workflows/cleanup.yml
index a8ed3ee27a..26b4d67826 100644
--- a/.github/workflows/cleanup.yml
+++ b/.github/workflows/cleanup.yml
@@ -9,7 +9,7 @@ on:
jobs:
cleanup_release:
- uses: blockscout/blockscout-ci-cd/.github/workflows/cleanup_helmfile.yaml@master
+ uses: blockscout/actions/.github/workflows/cleanup_helmfile.yaml@main
with:
appName: review-l2-$GITHUB_REF_NAME_SLUG
globalEnv: review
@@ -18,7 +18,7 @@ jobs:
vaultRole: ci-dev
secrets: inherit
cleanup_l2_release:
- uses: blockscout/blockscout-ci-cd/.github/workflows/cleanup_helmfile.yaml@master
+ uses: blockscout/actions/.github/workflows/cleanup_helmfile.yaml@main
with:
appName: review-$GITHUB_REF_NAME_SLUG
globalEnv: review
diff --git a/.github/workflows/deploy-review-l2.yml b/.github/workflows/deploy-review-l2.yml
index a625a51b85..721274589a 100644
--- a/.github/workflows/deploy-review-l2.yml
+++ b/.github/workflows/deploy-review-l2.yml
@@ -21,6 +21,7 @@ on:
- eth_sepolia
- eth_goerli
- filecoin
+ - immutable
- neon_devnet
- optimism
- optimism_celestia
@@ -62,7 +63,7 @@ jobs:
deploy_review_l2:
name: Deploy frontend (L2)
needs: [ make_slug, publish_image ]
- uses: blockscout/blockscout-ci-cd/.github/workflows/deploy_helmfile.yaml@master
+ uses: blockscout/actions/.github/workflows/deploy_helmfile.yaml@main
with:
appName: review-l2-${{ needs.make_slug.outputs.REF_SLUG }}
globalEnv: review
diff --git a/.github/workflows/deploy-review.yml b/.github/workflows/deploy-review.yml
index fb5ae2abdd..7922332bf7 100644
--- a/.github/workflows/deploy-review.yml
+++ b/.github/workflows/deploy-review.yml
@@ -21,6 +21,7 @@ on:
- eth_sepolia
- eth_goerli
- filecoin
+ - immutable
- mekong
- neon_devnet
- optimism
@@ -64,7 +65,7 @@ jobs:
deploy_review:
name: Deploy frontend
needs: [ make_slug, publish_image ]
- uses: blockscout/blockscout-ci-cd/.github/workflows/deploy_helmfile.yaml@master
+ uses: blockscout/actions/.github/workflows/deploy_helmfile.yaml@main
with:
appName: review-${{ needs.make_slug.outputs.REF_SLUG }}
globalEnv: review
diff --git a/.github/workflows/e2e-tests.yml b/.github/workflows/e2e-tests.yml
index 265f55ba60..b4ed448440 100644
--- a/.github/workflows/e2e-tests.yml
+++ b/.github/workflows/e2e-tests.yml
@@ -50,5 +50,27 @@ jobs:
test:
name: Run tests
needs: deploy_e2e
- uses: blockscout/blockscout-ci-cd/.github/workflows/e2e_new.yaml@master
- secrets: inherit
\ No newline at end of file
+ runs-on: ubuntu-latest
+ permissions: write-all
+ steps:
+ - name: Get Vault credentials
+ id: retrieve-vault-secrets
+ uses: hashicorp/vault-action@v2.4.1
+ with:
+ url: https://vault.k8s.blockscout.com
+ role: ci-dev
+ path: github-jwt
+ method: jwt
+ tlsSkipVerify: false
+ exportToken: true
+ secrets: |
+ ci/data/dev/github token | WORKFLOW_TRIGGER_TOKEN ;
+ - name: Trigger tests
+ uses: convictional/trigger-workflow-and-wait@v1.6.1
+ with:
+ owner: blockscout
+ repo: blockscout-ci-cd
+ github_token: ${{ env.WORKFLOW_TRIGGER_TOKEN }}
+ workflow_file_name: e2e_new.yaml
+ ref: master
+ wait_interval: 30
diff --git a/.gitignore b/.gitignore
index d4459df0b2..e61963b528 100644
--- a/.gitignore
+++ b/.gitignore
@@ -16,6 +16,7 @@
/public/assets/configs
/public/icons/sprite.svg
/public/icons/sprite.*.svg
+/public/icons/registry.json
/public/icons/README.md
/public/static/og_image.png
/public/sitemap.xml
diff --git a/.vscode/tasks.json b/.vscode/tasks.json
index 8cf832c905..16fcb132fc 100644
--- a/.vscode/tasks.json
+++ b/.vscode/tasks.json
@@ -362,20 +362,25 @@
"arbitrum",
"arbitrum_sepolia",
"base",
+ "blackfort_testnet",
"celo_alfajores",
"garnet",
"gnosis",
+ "immutable",
"eth",
"eth_goerli",
"eth_sepolia",
"filecoin",
"mekong",
+ "neon_devnet",
"optimism",
"optimism_celestia",
+ "optimism_interop_0",
"optimism_sepolia",
"polygon",
"rari_testnet",
"rootstock_testnet",
+ "scroll_sepolia",
"shibarium",
"stability_testnet",
"zkevm",
diff --git a/Dockerfile b/Dockerfile
index 36eadc261a..beada8501c 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -9,7 +9,12 @@ RUN ln -sf /usr/bin/python3 /usr/bin/python
### APP
# Install dependencies
WORKDIR /app
-COPY package.json yarn.lock ./
+COPY package.json yarn.lock tsconfig.json ./
+COPY types ./types
+COPY lib ./lib
+COPY configs/app ./configs/app
+COPY toolkit/theme ./toolkit/theme
+COPY ui/shared/forms/validators/url.ts ./ui/shared/forms/validators/url.ts
RUN apk add git
RUN yarn --frozen-lockfile --network-timeout 100000
@@ -44,7 +49,7 @@ RUN yarn --frozen-lockfile --network-timeout 100000
# ****** STAGE 2: Build *******
# *****************************
FROM node:22.11.0-alpine AS builder
-RUN apk add --no-cache --upgrade libc6-compat bash
+RUN apk add --no-cache --upgrade libc6-compat bash jq
# pass build args to env variables
ARG GIT_COMMIT_SHA
diff --git a/configs/app/app.ts b/configs/app/app.ts
index 8debdab642..f5cca629e7 100644
--- a/configs/app/app.ts
+++ b/configs/app/app.ts
@@ -10,10 +10,12 @@ const baseUrl = [
appPort && ':' + appPort,
].filter(Boolean).join('');
const isDev = getEnvValue('NEXT_PUBLIC_APP_ENV') === 'development';
+const isPw = getEnvValue('NEXT_PUBLIC_APP_INSTANCE') === 'pw';
const spriteHash = getEnvValue('NEXT_PUBLIC_ICON_SPRITE_HASH');
const app = Object.freeze({
isDev,
+ isPw,
protocol: appSchema,
host: appHost,
port: appPort,
diff --git a/configs/app/features/rollup.ts b/configs/app/features/rollup.ts
index 6dc4b2e9d2..bdd46393aa 100644
--- a/configs/app/features/rollup.ts
+++ b/configs/app/features/rollup.ts
@@ -35,6 +35,7 @@ const config: Feature<{
type: RollupType;
homepage: { showLatestBlocks: boolean };
outputRootsEnabled: boolean;
+ interopEnabled: boolean;
L2WithdrawalUrl: string | undefined;
parentChain: ParentChain;
DA: {
@@ -50,6 +51,7 @@ const config: Feature<{
type,
L2WithdrawalUrl: type === 'optimistic' ? L2WithdrawalUrl : undefined,
outputRootsEnabled: type === 'optimistic' && getEnvValue('NEXT_PUBLIC_ROLLUP_OUTPUT_ROOTS_ENABLED') === 'true',
+ interopEnabled: type === 'optimistic' && getEnvValue('NEXT_PUBLIC_INTEROP_ENABLED') === 'true',
homepage: {
showLatestBlocks: getEnvValue('NEXT_PUBLIC_ROLLUP_HOMEPAGE_SHOW_LATEST_BLOCKS') === 'true',
},
diff --git a/configs/envs/.env.base b/configs/envs/.env.base
index 8eab44c966..3081968dd9 100644
--- a/configs/envs/.env.base
+++ b/configs/envs/.env.base
@@ -10,37 +10,41 @@ NEXT_PUBLIC_APP_ENV=development
NEXT_PUBLIC_API_WEBSOCKET_PROTOCOL=ws
# Instance ENVs
-NEXT_PUBLIC_AD_ADBUTLER_CONFIG_DESKTOP={ "id": "728301", "width": "728", "height": "90" }
-NEXT_PUBLIC_AD_ADBUTLER_CONFIG_MOBILE={ "id": "728302", "width": "320", "height": "100" }
-NEXT_PUBLIC_AD_BANNER_ADDITIONAL_PROVIDER=adbutler
NEXT_PUBLIC_ADMIN_SERVICE_API_HOST=https://admin-rs.services.blockscout.com
NEXT_PUBLIC_API_BASE_PATH=/
NEXT_PUBLIC_API_HOST=base.blockscout.com
NEXT_PUBLIC_API_SPEC_URL=https://raw.githubusercontent.com/blockscout/blockscout-api-v2-swagger/main/swagger.yaml
NEXT_PUBLIC_CONTRACT_CODE_IDES=[{'title':'Remix IDE','url':'https://remix.ethereum.org/?address={hash}&blockscout={domain}','icon_url':'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/ide-icons/remix.png'}]
NEXT_PUBLIC_CONTRACT_INFO_API_HOST=https://contracts-info.services.blockscout.com
-NEXT_PUBLIC_DEFI_DROPDOWN_ITEMS=[{'text':'Swap','icon':'swap','dappId':'aerodrome'},{'text':'Payment link','icon':'payment_link','dappId':'peanut-protocol'},{'text':'Get gas','icon':'gas','dappId':'smol-refuel'}]
+NEXT_PUBLIC_DEFI_DROPDOWN_ITEMS=[{'text':'Swap','icon':'swap','dappId':'rubic'},{'text':'Disperse','icon':'txn_batches_slim','dappId':'smol'},{'text':'Payment link','icon':'payment_link','dappId':'peanut-protocol'},{'text':'Get gas','icon':'gas','dappId':'smol-refuel'}]
+NEXT_PUBLIC_DEX_POOLS_ENABLED=true
+NEXT_PUBLIC_FAULT_PROOF_ENABLED=true
NEXT_PUBLIC_FEATURED_NETWORKS=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/featured-networks/base-mainnet.json
NEXT_PUBLIC_FOOTER_LINKS=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/footer-links/base-mainnet.json
+NEXT_PUBLIC_GAME_BADGE_CLAIM_LINK=https://badges.blockscout.com/mint/sherblockHolmesBadge
+NEXT_PUBLIC_GAS_REFUEL_PROVIDER_CONFIG={'name': 'Need gas?', 'url_template': 'https://smolrefuel.com/?outboundChain={chainId}&partner=blockscout&utm_source=blockscout&disableBridges=true', 'dapp_id': 'smol-refuel', 'logo': 'https://blockscout-content.s3.amazonaws.com/smolrefuel-logo-action-button.png'}
NEXT_PUBLIC_GRAPHIQL_TRANSACTION=0xfd5c5dae7b69fe29e61d19b9943e688aa0f1be1e983c4fba8fe985f90ff69d5f
+NEXT_PUBLIC_HAS_CONTRACT_AUDIT_REPORTS=true
NEXT_PUBLIC_HAS_USER_OPS=true
NEXT_PUBLIC_HIDE_INDEXING_ALERT_INT_TXS=true
NEXT_PUBLIC_HOMEPAGE_CHARTS=['daily_txs']
-NEXT_PUBLIC_HOMEPAGE_PLATE_BACKGROUND=linear-gradient(136.9deg,rgb(107 94 236) 1.5%,rgb(0 82 255) 56.84%,rgb(82 62 231) 98.54%)
+NEXT_PUBLIC_HOMEPAGE_HERO_BANNER_CONFIG={'background':['linear-gradient(136.9deg,rgb(107 94 236) 1.5%,rgb(0 82 255) 56.84%,rgb(82 62 231) 98.54%)']}
NEXT_PUBLIC_IS_ACCOUNT_SUPPORTED=true
NEXT_PUBLIC_LOGOUT_URL=https://basechain.us.auth0.com/v2/logout
-NEXT_PUBLIC_MARKETPLACE_BANNER_CONTENT_URL=https://gist.githubusercontent.com/maxaleks/0d18fc309ff499075127b364cc69306d/raw/2a51f961a8c1b9f884f2ab7eed79d4b69330e1ae/peanut_protocol_banner.html
-NEXT_PUBLIC_MARKETPLACE_BANNER_LINK_URL=https://base.blockscout.com/apps/peanut-protocol
+NEXT_PUBLIC_MAINTENANCE_ALERT_MESSAGE=
Joined recent campaigns? Mint your Merit Badge here
+NEXT_PUBLIC_MARKETPLACE_BANNER_CONTENT_URL=https://gist.githubusercontent.com/maikReal/974c47f86a3158c1a86b092ae2f044b3/raw/abcc7e02150cd85d4974503a0357162c0a2c35a9/merits-banner.html
+NEXT_PUBLIC_MARKETPLACE_BANNER_LINK_URL=https://swap.blockscout.com?utm_source=blockscout&utm_medium=base
NEXT_PUBLIC_MARKETPLACE_CATEGORIES_URL=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/marketplace-categories/default.json
NEXT_PUBLIC_MARKETPLACE_ENABLED=true
+NEXT_PUBLIC_MARKETPLACE_RATING_AIRTABLE_BASE_ID=appGkvtmKI7fXE4Vs
NEXT_PUBLIC_MARKETPLACE_SECURITY_REPORTS_URL=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/marketplace-security-reports/default.json
NEXT_PUBLIC_MARKETPLACE_SUBMIT_FORM=https://airtable.com/appiy5yijZpMMSKjT/shr6uMGPKjj1DK7NL
NEXT_PUBLIC_MARKETPLACE_SUGGEST_IDEAS_FORM=https://airtable.com/appiy5yijZpMMSKjT/pag3t82DUCyhGRZZO/form
NEXT_PUBLIC_METADATA_SERVICE_API_HOST=https://metadata.services.blockscout.com
NEXT_PUBLIC_METASUITES_ENABLED=true
-NEXT_PUBLIC_MULTICHAIN_BALANCE_PROVIDER_CONFIG=[{'name': 'zerion', 'dapp_id': 'zerion', 'url_template': 'https://app.zerion.io/{address}/overview?utm_source=blockscout&utm_medium=address', 'logo': 'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/marketplace-logos/zerion.svg'}]
+NEXT_PUBLIC_MULTICHAIN_BALANCE_PROVIDER_CONFIG=[{'name': 'zerion', 'url_template': 'https://app.zerion.io/{address}/overview', 'logo': 'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/marketplace-logos/zerion.svg'},{'name': 'zapper', 'url_template': 'https://zapper.xyz/account/{address}', 'logo': 'https://blockscout-content.s3.amazonaws.com/zapper-icon.png'}]
NEXT_PUBLIC_NAME_SERVICE_API_HOST=https://bens.services.blockscout.com
-NEXT_PUBLIC_NAVIGATION_HIGHLIGHTED_ROUTES=['/apps']
+NEXT_PUBLIC_NAVIGATION_HIGHLIGHTED_ROUTES=['/pools']
NEXT_PUBLIC_NETWORK_CURRENCY_DECIMALS=18
NEXT_PUBLIC_NETWORK_CURRENCY_NAME=Ether
NEXT_PUBLIC_NETWORK_CURRENCY_SYMBOL=ETH
@@ -59,8 +63,10 @@ NEXT_PUBLIC_ROLLUP_L1_BASE_URL=https://eth.blockscout.com/
NEXT_PUBLIC_ROLLUP_L2_WITHDRAWAL_URL=https://bridge.base.org/withdraw
NEXT_PUBLIC_ROLLUP_TYPE=optimistic
NEXT_PUBLIC_SAFE_TX_SERVICE_URL=https://safe-transaction-base.safe.global
-NEXT_PUBLIC_STATS_API_HOST=https://stats-l2-base-mainnet.k8s-prod-1.blockscout.com
+NEXT_PUBLIC_STATS_API_HOST=https://stats-l2-base-mainnet.k8s-prod-2.blockscout.com
NEXT_PUBLIC_TRANSACTION_INTERPRETATION_PROVIDER=blockscout
NEXT_PUBLIC_VIEWS_ADDRESS_IDENTICON_TYPE=gradient_avatar
NEXT_PUBLIC_VIEWS_CONTRACT_SOLIDITYSCAN_ENABLED=true
-NEXT_PUBLIC_VISUALIZE_API_HOST=https://visualizer.services.blockscout.com
\ No newline at end of file
+NEXT_PUBLIC_VIEWS_NFT_MARKETPLACES=[{'name':'OpenSea','collection_url':'https://opensea.io/assets/base/{hash}','instance_url':'https://opensea.io/assets/base/{hash}/{id}','logo_url':'https://opensea.io/static/images/logos/opensea-logo.svg'}]
+NEXT_PUBLIC_VISUALIZE_API_HOST=https://visualizer.services.blockscout.com
+NEXT_PUBLIC_XSTAR_SCORE_URL=https://docs.xname.app/the-solution-adaptive-proof-of-humanity-on-blockchain/xhs-scoring-algorithm?utm_source=blockscout&utm_medium=address
\ No newline at end of file
diff --git a/configs/envs/.env.celo_alfajores b/configs/envs/.env.celo_alfajores
index d93f9e36ae..e0f9fb1020 100644
--- a/configs/envs/.env.celo_alfajores
+++ b/configs/envs/.env.celo_alfajores
@@ -9,18 +9,21 @@ NEXT_PUBLIC_APP_PORT=3000
NEXT_PUBLIC_APP_ENV=development
NEXT_PUBLIC_API_WEBSOCKET_PROTOCOL=ws
-NEXT_PUBLIC_CELO_ENABLED=true
-NEXT_PUBLIC_CELO_L2_UPGRADE_BLOCK=26369280
-
# Instance ENVs
+NEXT_PUBLIC_ADMIN_SERVICE_API_HOST=https://admin-rs.services.blockscout.com
NEXT_PUBLIC_API_BASE_PATH=/
NEXT_PUBLIC_API_HOST=celo-alfajores.blockscout.com
NEXT_PUBLIC_API_SPEC_URL=https://raw.githubusercontent.com/blockscout/blockscout-api-v2-swagger/main/swagger.yaml
+NEXT_PUBLIC_CELO_ENABLED=true
+NEXT_PUBLIC_CELO_L2_UPGRADE_BLOCK=26369280
NEXT_PUBLIC_CONTRACT_CODE_IDES=[{'title':'Remix IDE','url':'https://remix.ethereum.org/?address={hash}&blockscout={domain}','icon_url':'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/ide-icons/remix.png'}]
+NEXT_PUBLIC_CONTRACT_INFO_API_HOST=https://contracts-info.services.blockscout.com
+NEXT_PUBLIC_FEATURED_NETWORKS=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/featured-networks/celo-alfajores-testnet.json
+NEXT_PUBLIC_GAME_BADGE_CLAIM_LINK=https://badges.blockscout.com/mint/sherblockHolmesBadge
NEXT_PUBLIC_GAS_TRACKER_ENABLED=false
+NEXT_PUBLIC_GRAPHIQL_TRANSACTION=0x9767ce30754afad2a3279b9df2d13257f467c3dad4e0e601271e66d16dfd1641
NEXT_PUBLIC_HOMEPAGE_CHARTS=['daily_txs']
-NEXT_PUBLIC_HOMEPAGE_PLATE_BACKGROUND=rgba(252, 255, 82, 1)
-NEXT_PUBLIC_HOMEPAGE_PLATE_TEXT_COLOR=rgba(0, 0, 0, 1)
+NEXT_PUBLIC_HOMEPAGE_HERO_BANNER_CONFIG={'background':['rgba(252, 255, 82, 1)'],'text_color':['rgba(0, 0, 0, 1)']}
NEXT_PUBLIC_IS_TESTNET=true
NEXT_PUBLIC_MARKETPLACE_ENABLED=false
NEXT_PUBLIC_METADATA_SERVICE_API_HOST=https://metadata.services.blockscout.com
@@ -32,12 +35,14 @@ NEXT_PUBLIC_NETWORK_ICON_DARK=https://raw.githubusercontent.com/blockscout/front
NEXT_PUBLIC_NETWORK_ID=44787
NEXT_PUBLIC_NETWORK_LOGO=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-logos/celo-logo-light.svg
NEXT_PUBLIC_NETWORK_LOGO_DARK=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-logos/celo-logo-dark.svg
+NEXT_PUBLIC_NETWORK_MULTIPLE_GAS_CURRENCIES=true
NEXT_PUBLIC_NETWORK_NAME=Celo Alfajores
NEXT_PUBLIC_NETWORK_RPC_URL=https://alfajores-forno.celo-testnet.org
NEXT_PUBLIC_NETWORK_SHORT_NAME=Alfajores
NEXT_PUBLIC_OG_ENHANCED_DATA_ENABLED=true
NEXT_PUBLIC_OG_IMAGE_URL=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/og-images/celo.png
+NEXT_PUBLIC_STATS_API_HOST=https://stats-alfajores-testnet.k8s-prod-1.blockscout.com
NEXT_PUBLIC_TRANSACTION_INTERPRETATION_PROVIDER=blockscout
+NEXT_PUBLIC_VIEWS_BLOCK_HIDDEN_FIELDS=['burnt_fees']
NEXT_PUBLIC_VIEWS_CONTRACT_SOLIDITYSCAN_ENABLED=true
-NEXT_PUBLIC_VISUALIZE_API_HOST=https://visualizer.services.blockscout.com
-NEXT_PUBLIC_HOMEPAGE_STATS=['total_blocks','average_block_time','total_txs','wallet_addresses','gas_tracker','current_epoch']
\ No newline at end of file
+NEXT_PUBLIC_VISUALIZE_API_HOST=https://visualizer.services.blockscout.com
\ No newline at end of file
diff --git a/configs/envs/.env.eth b/configs/envs/.env.eth
index 215b86e5d1..b1d1c3d658 100644
--- a/configs/envs/.env.eth
+++ b/configs/envs/.env.eth
@@ -10,9 +10,6 @@ NEXT_PUBLIC_APP_ENV=development
NEXT_PUBLIC_API_WEBSOCKET_PROTOCOL=ws
# Instance ENVs
-NEXT_PUBLIC_AD_ADBUTLER_CONFIG_DESKTOP={ "id": "728471", "width": "728", "height": "90" }
-NEXT_PUBLIC_AD_ADBUTLER_CONFIG_MOBILE={ "id": "728470", "width": "320", "height": "100" }
-NEXT_PUBLIC_AD_BANNER_ADDITIONAL_PROVIDER=adbutler
NEXT_PUBLIC_ADMIN_SERVICE_API_HOST=https://admin-rs.services.blockscout.com
NEXT_PUBLIC_API_BASE_PATH=/
NEXT_PUBLIC_API_HOST=eth.blockscout.com
@@ -20,9 +17,11 @@ NEXT_PUBLIC_API_SPEC_URL=https://raw.githubusercontent.com/blockscout/blockscout
NEXT_PUBLIC_CONTRACT_CODE_IDES=[{'title':'Remix IDE','url':'https://remix.ethereum.org/?address={hash}&blockscout={domain}','icon_url':'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/ide-icons/remix.png'}]
NEXT_PUBLIC_CONTRACT_INFO_API_HOST=https://contracts-info.services.blockscout.com
NEXT_PUBLIC_DATA_AVAILABILITY_ENABLED=true
-NEXT_PUBLIC_DEFI_DROPDOWN_ITEMS=[{'text':'Swapscout','icon':'swap','dappId':'swapscout'},{'text':'Disperse','icon':'txn_batches_slim','dappId':'smol'},{'text':'Payment link','icon':'payment_link','dappId':'peanut-protocol'},{'text':'Get gas','icon':'gas','dappId':'smol-refuel'}]
+NEXT_PUBLIC_DEFI_DROPDOWN_ITEMS=[{'text':'Swapscout','icon':'swap','dappId':'swapscout'},{'text':'Payment link','icon':'payment_link','dappId':'peanut-protocol'},{'text':'Get gas','icon':'gas','dappId':'smol-refuel'}]
+NEXT_PUBLIC_DEX_POOLS_ENABLED=true
NEXT_PUBLIC_FEATURED_NETWORKS=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/featured-networks/eth.json
NEXT_PUBLIC_FOOTER_LINKS=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/footer-links/eth-mainnet.json
+NEXT_PUBLIC_GAME_BADGE_CLAIM_LINK=https://badges.blockscout.com/mint/sherblockHolmesBadge
NEXT_PUBLIC_GAS_REFUEL_PROVIDER_CONFIG={'name': 'Need gas?', 'url_template': 'https://smolrefuel.com/?outboundChain={chainId}&partner=blockscout&utm_source=blockscout&disableBridges=true', 'dapp_id': 'smol-refuel', 'logo': 'https://blockscout-content.s3.amazonaws.com/smolrefuel-logo-action-button.png'}
NEXT_PUBLIC_GRAPHIQL_TRANSACTION=0xd01175f1efa23f36c5579b3c13e2bbd0885017643a7efef5cbcb6b474384dfa8
NEXT_PUBLIC_HAS_BEACON_CHAIN=true
@@ -32,7 +31,7 @@ NEXT_PUBLIC_HIDE_INDEXING_ALERT_BLOCKS=true
NEXT_PUBLIC_HOMEPAGE_CHARTS=['daily_txs', 'coin_price', 'market_cap']
NEXT_PUBLIC_IS_ACCOUNT_SUPPORTED=true
NEXT_PUBLIC_LOGOUT_URL=https://ethereum-mainnet.us.auth0.com/v2/logout
-NEXT_PUBLIC_MAINTENANCE_ALERT_MESSAGE=Participated in our recent Blockscout activities? Check your eligibility and claim your NFT Scout badges. More exciting things are coming soon!
+NEXT_PUBLIC_MAINTENANCE_ALERT_MESSAGE=Joined recent campaigns? Mint your Merit Badge here
NEXT_PUBLIC_MARKETPLACE_BANNER_CONTENT_URL=https://gist.githubusercontent.com/maikReal/974c47f86a3158c1a86b092ae2f044b3/raw/abcc7e02150cd85d4974503a0357162c0a2c35a9/merits-banner.html
NEXT_PUBLIC_MARKETPLACE_BANNER_LINK_URL=https://swap.blockscout.com?utm_source=blockscout&utm_medium=eth
NEXT_PUBLIC_MARKETPLACE_CATEGORIES_URL=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/marketplace-categories/default.json
@@ -45,11 +44,11 @@ NEXT_PUBLIC_METADATA_SERVICE_API_HOST=https://metadata.services.blockscout.com
NEXT_PUBLIC_METASUITES_ENABLED=true
NEXT_PUBLIC_MULTICHAIN_BALANCE_PROVIDER_CONFIG=[{'name': 'zerion', 'url_template': 'https://app.zerion.io/{address}/overview', 'logo': 'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/marketplace-logos/zerion.svg'},{'name': 'zapper', 'url_template': 'https://zapper.xyz/account/{address}', 'logo': 'https://blockscout-content.s3.amazonaws.com/zapper-icon.png'}]
NEXT_PUBLIC_NAME_SERVICE_API_HOST=https://bens.services.blockscout.com
-NEXT_PUBLIC_NAVIGATION_HIGHLIGHTED_ROUTES=['/apps']
+NEXT_PUBLIC_NAVIGATION_HIGHLIGHTED_ROUTES=['/pools']
NEXT_PUBLIC_NETWORK_CURRENCY_DECIMALS=18
NEXT_PUBLIC_NETWORK_CURRENCY_NAME=Ether
NEXT_PUBLIC_NETWORK_CURRENCY_SYMBOL=ETH
-NEXT_PUBLIC_NETWORK_EXPLORERS=[{'title':'GeckoTerminal','logo':'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/explorer-logos/geckoterminal.png','baseUrl':'https://www.geckoterminal.com/','paths':{'token':'/eth/pools'}},{'title':'Etherscan','logo':'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/explorer-logos/etherscan.png','baseUrl':'https://etherscan.io/','paths':{'tx':'/tx','address':'/address','token':'/token','block':'/block'}}, {'title':'Blockchair','logo':'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/explorer-logos/blockchair.png','baseUrl':'https://blockchair.com/','paths':{'tx':'/ethereum/transaction','address':'/ethereum/address','token':'/ethereum/erc-20/token','block':'/ethereum/block'}},{'title':'Sentio','logo':'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/explorer-logos/sentio.png','baseUrl':'https://app.sentio.xyz/','paths':{'tx':'/tx/1','address':'/contract/1'}}, {'title':'Tenderly','logo':'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/explorer-logos/tenderly.png','baseUrl':'https://dashboard.tenderly.co','paths':{'tx':'/tx/mainnet'}}, {'title':'0xPPL','logo':'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/explorer-logos/0xPPL.png','baseUrl':'https://0xppl.com','paths':{'tx':'/Ethereum/tx','address':'/','token':'/c/Ethereum'}}, {'title':'3xpl','logo':'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/explorer-logos/3xpl.png','baseUrl':'https://3xpl.com/','paths':{'tx':'/ethereum/transaction','address':'/ethereum/address'}} ]
+NEXT_PUBLIC_NETWORK_EXPLORERS=[{'title':'Moralis','logo':'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/explorer-logos/moralis.png','baseUrl':'https://moralis.com/','paths':{'token':'/chain/ethereum/token/price'}},{'title':'GeckoTerminal','logo':'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/explorer-logos/geckoterminal.png','baseUrl':'https://www.geckoterminal.com/','paths':{'token':'/eth/pools'}},{'title':'Etherscan','logo':'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/explorer-logos/etherscan.png','baseUrl':'https://etherscan.io/','paths':{'tx':'/tx','address':'/address','token':'/token','block':'/block'}}, {'title':'Blockchair','logo':'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/explorer-logos/blockchair.png','baseUrl':'https://blockchair.com/','paths':{'tx':'/ethereum/transaction','address':'/ethereum/address','token':'/ethereum/erc-20/token','block':'/ethereum/block'}},{'title':'Sentio','logo':'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/explorer-logos/sentio.png','baseUrl':'https://app.sentio.xyz/','paths':{'tx':'/tx/1','address':'/contract/1'}}, {'title':'Tenderly','logo':'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/explorer-logos/tenderly.png','baseUrl':'https://dashboard.tenderly.co','paths':{'tx':'/tx/mainnet'}}, {'title':'0xPPL','logo':'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/explorer-logos/0xPPL.png','baseUrl':'https://0xppl.com','paths':{'tx':'/Ethereum/tx','address':'/','token':'/c/Ethereum'}}, {'title':'3xpl','logo':'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/explorer-logos/3xpl.png','baseUrl':'https://3xpl.com/','paths':{'tx':'/ethereum/transaction','address':'/ethereum/address'}} ]
NEXT_PUBLIC_NETWORK_ID=1
NEXT_PUBLIC_NETWORK_NAME=Ethereum
NEXT_PUBLIC_NETWORK_RPC_URL=https://eth.drpc.org
@@ -61,10 +60,10 @@ NEXT_PUBLIC_OTHER_LINKS=[{'url':'https://eth.drpc.org?ref=559183','text':'Public
NEXT_PUBLIC_REWARDS_SERVICE_API_HOST=https://merits.blockscout.com
NEXT_PUBLIC_SAFE_TX_SERVICE_URL=https://safe-transaction-mainnet.safe.global
NEXT_PUBLIC_SAVE_ON_GAS_ENABLED=true
-NEXT_PUBLIC_SEO_ENHANCED_DATA_ENABLED=true
NEXT_PUBLIC_STATS_API_HOST=https://stats-eth-main.k8s-prod-1.blockscout.com
NEXT_PUBLIC_TRANSACTION_INTERPRETATION_PROVIDER=blockscout
NEXT_PUBLIC_VIEWS_CONTRACT_SOLIDITYSCAN_ENABLED=true
+NEXT_PUBLIC_VIEWS_NFT_MARKETPLACES=[{'name':'Rarible','collection_url':'https://rarible.com/collection/{hash}/items','instance_url':'https://rarible.com/token/{hash}:{id}','logo_url':'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/nft-marketplace-logos/rarible.png'},{'name':'Blur','collection_url':'https://blur.io/eth/collection/{hash}','instance_url':'https://blur.io/eth/asset/{hash}/{id}','logo_url':'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/nft-marketplace-logos/blur.png'},{'name':'Nftrade','collection_url':'https://nftrade.com/assets/eth/{hash}','instance_url':'https://nftrade.com/assets/eth/{hash}/{id}','logo_url':'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/nft-marketplace-logos/nftrade.png'},{'name':'MagicEden','collection_url':'https://magiceden.io/collections/ethereum/{hash}','instance_url':'https://magiceden.io/item-details/ethereum/{hash}/{id}','logo_url':'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/nft-marketplace-logos/magiceden.png'}]
+NEXT_PUBLIC_VIEWS_TOKEN_SCAM_TOGGLE_ENABLED=true
NEXT_PUBLIC_VISUALIZE_API_HOST=https://visualizer.services.blockscout.com
-NEXT_PUBLIC_XSTAR_SCORE_URL=https://docs.xname.app/the-solution-adaptive-proof-of-humanity-on-blockchain/xhs-scoring-algorithm?utm_source=blockscout&utm_medium=address
-NEXT_PUBLIC_GAME_BADGE_CLAIM_LINK=https://badges.blockscout.com/mint/hiddenBlockBadge
\ No newline at end of file
+NEXT_PUBLIC_XSTAR_SCORE_URL=https://docs.xname.app/the-solution-adaptive-proof-of-humanity-on-blockchain/xhs-scoring-algorithm?utm_source=blockscout&utm_medium=address
\ No newline at end of file
diff --git a/configs/envs/.env.eth_sepolia b/configs/envs/.env.eth_sepolia
index 8f73a35219..91fac060df 100644
--- a/configs/envs/.env.eth_sepolia
+++ b/configs/envs/.env.eth_sepolia
@@ -68,4 +68,4 @@ NEXT_PUBLIC_STATS_API_HOST=https://stats-sepolia.k8s-prod-2.blockscout.com
NEXT_PUBLIC_TRANSACTION_INTERPRETATION_PROVIDER=blockscout
NEXT_PUBLIC_VIEWS_CONTRACT_SOLIDITYSCAN_ENABLED=true
NEXT_PUBLIC_VISUALIZE_API_HOST=https://visualizer.services.blockscout.com
-NEXT_PUBLIC_XSTAR_SCORE_URL=https://docs.xname.app/the-solution-adaptive-proof-of-humanity-on-blockchain/xhs-scoring-algorithm?utm_source=blockscout&utm_medium=address
\ No newline at end of file
+NEXT_PUBLIC_XSTAR_SCORE_URL=https://docs.xname.app/the-solution-adaptive-proof-of-humanity-on-blockchain/xhs-scoring-algorithm?utm_source=blockscout&utm_medium=address
diff --git a/configs/envs/.env.immutable b/configs/envs/.env.immutable
new file mode 100644
index 0000000000..544c3b7df4
--- /dev/null
+++ b/configs/envs/.env.immutable
@@ -0,0 +1,53 @@
+# Set of ENVs for Immutable network explorer
+# https://explorer.immutable.com
+# This is an auto-generated file. To update all values, run "yarn dev:preset:sync --name=immutable"
+
+# Local ENVs
+NEXT_PUBLIC_APP_PROTOCOL=http
+NEXT_PUBLIC_APP_HOST=localhost
+NEXT_PUBLIC_APP_PORT=3000
+NEXT_PUBLIC_APP_ENV=development
+NEXT_PUBLIC_API_WEBSOCKET_PROTOCOL=ws
+
+# Instance ENVs
+NEXT_PUBLIC_AD_BANNER_PROVIDER=none
+NEXT_PUBLIC_AD_TEXT_PROVIDER=none
+NEXT_PUBLIC_ADMIN_SERVICE_API_HOST=https://admin-rs.services.blockscout.com
+NEXT_PUBLIC_API_BASE_PATH=/
+NEXT_PUBLIC_API_HOST=immutable-mainnet.blockscout.com
+NEXT_PUBLIC_API_SPEC_URL=https://raw.githubusercontent.com/blockscout/blockscout-api-v2-swagger/main/swagger.yaml
+NEXT_PUBLIC_CONTRACT_CODE_IDES=[{'title':'Remix IDE','url':'https://remix.ethereum.org/?address={hash}&blockscout={domain}','icon_url':'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/ide-icons/remix.png'}]
+NEXT_PUBLIC_CONTRACT_INFO_API_HOST=https://contracts-info.services.blockscout.com
+NEXT_PUBLIC_DEX_POOLS_ENABLED=true
+NEXT_PUBLIC_FEATURED_NETWORKS=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/featured-networks/immutable-mainnet.json
+NEXT_PUBLIC_FOOTER_LINKS=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/footer-links/immutable.json
+NEXT_PUBLIC_GAME_BADGE_CLAIM_LINK=https://badges.blockscout.com/mint/sherblockHolmesBadge
+NEXT_PUBLIC_GRAPHIQL_TRANSACTION=0x6166cece570f4731ccc94c2d17d854ce88496cd3b48e03b537959992ab6685c8
+NEXT_PUBLIC_HAS_CONTRACT_AUDIT_REPORTS=true
+NEXT_PUBLIC_HELIA_VERIFIED_FETCH_ENABLED=false
+NEXT_PUBLIC_HOMEPAGE_CHARTS=['daily_txs']
+NEXT_PUBLIC_HOMEPAGE_HERO_BANNER_CONFIG={'background':['no-repeat center/100% 100% url(https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-skins/immutable.jpg)'],'text_color':['rgba(19, 19, 19, 1)']}
+NEXT_PUBLIC_IS_ACCOUNT_SUPPORTED=true
+NEXT_PUBLIC_LOGOUT_URL=https://blockscout-immutable.us.auth0.com/v2/logout
+NEXT_PUBLIC_MARKETPLACE_ENABLED=false
+NEXT_PUBLIC_METADATA_SERVICE_API_HOST=https://metadata.services.blockscout.com
+NEXT_PUBLIC_NAVIGATION_HIGHLIGHTED_ROUTES=['/pools']
+NEXT_PUBLIC_NETWORK_CURRENCY_DECIMALS=18
+NEXT_PUBLIC_NETWORK_CURRENCY_NAME=IMX
+NEXT_PUBLIC_NETWORK_CURRENCY_SYMBOL=IMX
+NEXT_PUBLIC_NETWORK_EXPLORERS=[{'title':'GeckoTerminal','logo':'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/explorer-logos/geckoterminal.png','baseUrl':'https://www.geckoterminal.com/','paths':{'token':'/immutable-zkevm/pools'}}]
+NEXT_PUBLIC_NETWORK_ICON=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-icons/immutable-short.svg
+NEXT_PUBLIC_NETWORK_ID=13371
+NEXT_PUBLIC_NETWORK_LOGO=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-logos/immutable.svg
+NEXT_PUBLIC_NETWORK_NAME=Immutable
+NEXT_PUBLIC_NETWORK_RPC_URL=https://rpc.immutable.com/
+NEXT_PUBLIC_NETWORK_SHORT_NAME=Immutable
+NEXT_PUBLIC_OG_ENHANCED_DATA_ENABLED=true
+NEXT_PUBLIC_OG_IMAGE_URL=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/og-images/immutable.png
+NEXT_PUBLIC_STATS_API_HOST=https://stats-immutable-mainnet.k8s.blockscout.com
+NEXT_PUBLIC_TRANSACTION_INTERPRETATION_PROVIDER=blockscout
+NEXT_PUBLIC_VIEWS_BLOCK_HIDDEN_FIELDS=["miner"]
+NEXT_PUBLIC_VIEWS_CONTRACT_SOLIDITYSCAN_ENABLED=true
+NEXT_PUBLIC_VIEWS_NFT_MARKETPLACES=[{'name':'Rarible','collection_url':'https://rarible.com/collection/immutablex/{hash}/items','instance_url':'https://rarible.com/token/immutablex/{hash}:{id}','logo_url':'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/nft-marketplace-logos/rarible.png'}]
+NEXT_PUBLIC_VIEWS_TOKEN_SCAM_TOGGLE_ENABLED=true
+NEXT_PUBLIC_VISUALIZE_API_HOST=https://visualizer.services.blockscout.com
\ No newline at end of file
diff --git a/configs/envs/.env.optimism b/configs/envs/.env.optimism
index 8fd9911afc..04f2307aab 100644
--- a/configs/envs/.env.optimism
+++ b/configs/envs/.env.optimism
@@ -9,45 +9,49 @@ NEXT_PUBLIC_APP_PORT=3000
NEXT_PUBLIC_APP_ENV=development
NEXT_PUBLIC_API_WEBSOCKET_PROTOCOL=ws
+NEXT_PUBLIC_ROLLUP_OUTPUT_ROOTS_ENABLED=true
+
# Instance ENVs
-NEXT_PUBLIC_AD_ADBUTLER_CONFIG_DESKTOP={ "id": "749780", "width": "728", "height": "90" }
-NEXT_PUBLIC_AD_ADBUTLER_CONFIG_MOBILE={ "id": "749779", "width": "320", "height": "100" }
-NEXT_PUBLIC_AD_BANNER_ADDITIONAL_PROVIDER=adbutler
NEXT_PUBLIC_ADMIN_SERVICE_API_HOST=https://admin-rs.services.blockscout.com
NEXT_PUBLIC_API_BASE_PATH=/
NEXT_PUBLIC_API_HOST=optimism.blockscout.com
NEXT_PUBLIC_API_SPEC_URL=https://raw.githubusercontent.com/blockscout/blockscout-api-v2-swagger/main/swagger.yaml
NEXT_PUBLIC_CONTRACT_CODE_IDES=[{'title':'Remix IDE','url':'https://remix.ethereum.org/?address={hash}&blockscout={domain}','icon_url':'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/ide-icons/remix.png'}]
NEXT_PUBLIC_CONTRACT_INFO_API_HOST=https://contracts-info.services.blockscout.com
-NEXT_PUBLIC_DEFI_DROPDOWN_ITEMS=[{'text':'Swap','icon':'swap','dappId':'velodrome'},{'text':'Payment link','icon':'payment_link','dappId':'peanut-protocol'},{'text':'Get gas','icon':'gas','dappId':'smol-refuel'}]
+NEXT_PUBLIC_DEFI_DROPDOWN_ITEMS=[{'text':'Swap','icon':'swap','dappId':'rubic'},{'text':'Disperse','icon':'txn_batches_slim','dappId':'smol'},{'text':'Payment link','icon':'payment_link','dappId':'peanut-protocol'},{'text':'Get gas','icon':'gas','dappId':'smol-refuel'}]
+NEXT_PUBLIC_DEX_POOLS_ENABLED=true
NEXT_PUBLIC_FAULT_PROOF_ENABLED=true
NEXT_PUBLIC_FEATURED_NETWORKS=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/featured-networks/optimism-mainnet.json
NEXT_PUBLIC_FOOTER_LINKS=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/footer-links/optimism.json
+NEXT_PUBLIC_GAME_BADGE_CLAIM_LINK=https://badges.blockscout.com/mint/sherblockHolmesBadge
+NEXT_PUBLIC_GAS_REFUEL_PROVIDER_CONFIG={'name': 'Need gas?', 'url_template': 'https://smolrefuel.com/?outboundChain={chainId}&partner=blockscout&utm_source=blockscout&disableBridges=true', 'dapp_id': 'smol-refuel', 'logo': 'https://blockscout-content.s3.amazonaws.com/smolrefuel-logo-action-button.png'}
NEXT_PUBLIC_GRAPHIQL_TRANSACTION=0x97f34a4cf685e365460dd38dbe16e092d8e4cc4b6ac779e3abcf4c18df6b1329
+NEXT_PUBLIC_HAS_CONTRACT_AUDIT_REPORTS=true
NEXT_PUBLIC_HAS_USER_OPS=true
NEXT_PUBLIC_HOMEPAGE_CHARTS=['daily_txs', 'coin_price', 'market_cap', 'secondary_coin_price']
-NEXT_PUBLIC_HOMEPAGE_PLATE_BACKGROUND=linear-gradient(90deg, rgb(232, 52, 53) 0%, rgb(139, 28, 232) 100%)
-NEXT_PUBLIC_HOMEPAGE_PLATE_TEXT_COLOR=rgb(255, 255, 255)
+NEXT_PUBLIC_HOMEPAGE_HERO_BANNER_CONFIG={'background':['linear-gradient(90deg, rgb(232, 52, 53) 0%, rgb(139, 28, 232) 100%)'],'text_color':['rgb(255, 255, 255)']}
NEXT_PUBLIC_IS_ACCOUNT_SUPPORTED=true
NEXT_PUBLIC_LOGOUT_URL=https://optimism-goerli.us.auth0.com/v2/logout
-NEXT_PUBLIC_MARKETPLACE_BANNER_CONTENT_URL=https://gist.githubusercontent.com/maxaleks/0d18fc309ff499075127b364cc69306d/raw/2a51f961a8c1b9f884f2ab7eed79d4b69330e1ae/peanut_protocol_banner.html
-NEXT_PUBLIC_MARKETPLACE_BANNER_LINK_URL=https://optimism.blockscout.com/apps/peanut-protocol
+NEXT_PUBLIC_MAINTENANCE_ALERT_MESSAGE=Joined recent campaigns? Mint your Merit Badge here
+NEXT_PUBLIC_MARKETPLACE_BANNER_CONTENT_URL=https://gist.githubusercontent.com/maikReal/974c47f86a3158c1a86b092ae2f044b3/raw/abcc7e02150cd85d4974503a0357162c0a2c35a9/merits-banner.html
+NEXT_PUBLIC_MARKETPLACE_BANNER_LINK_URL=https://swap.blockscout.com?utm_source=blockscout&utm_medium=optimism
NEXT_PUBLIC_MARKETPLACE_CATEGORIES_URL=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/marketplace-categories/default.json
NEXT_PUBLIC_MARKETPLACE_ENABLED=true
+NEXT_PUBLIC_MARKETPLACE_RATING_AIRTABLE_BASE_ID=appGkvtmKI7fXE4Vs
NEXT_PUBLIC_MARKETPLACE_SECURITY_REPORTS_URL=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/marketplace-security-reports/default.json
NEXT_PUBLIC_MARKETPLACE_SUBMIT_FORM=https://airtable.com/appiy5yijZpMMSKjT/shr6uMGPKjj1DK7NL
NEXT_PUBLIC_MARKETPLACE_SUGGEST_IDEAS_FORM=https://airtable.com/appiy5yijZpMMSKjT/pag3t82DUCyhGRZZO/form
NEXT_PUBLIC_METADATA_SERVICE_API_HOST=https://metadata.services.blockscout.com
NEXT_PUBLIC_METASUITES_ENABLED=true
-NEXT_PUBLIC_MULTICHAIN_BALANCE_PROVIDER_CONFIG=[{'name': 'zerion', 'dapp_id': 'zerion', 'url_template': 'https://app.zerion.io/{address}/overview?utm_source=blockscout&utm_medium=address', 'logo': 'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/marketplace-logos/zerion.svg'}]
+NEXT_PUBLIC_MULTICHAIN_BALANCE_PROVIDER_CONFIG=[{'name': 'zerion', 'url_template': 'https://app.zerion.io/{address}/overview?utm_source=blockscout&utm_medium=address', 'logo': 'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/marketplace-logos/zerion.svg'}]
NEXT_PUBLIC_NAME_SERVICE_API_HOST=https://bens.services.blockscout.com
-NEXT_PUBLIC_NAVIGATION_HIGHLIGHTED_ROUTES=['/apps']
+NEXT_PUBLIC_NAVIGATION_HIGHLIGHTED_ROUTES=['/pools']
NEXT_PUBLIC_NETWORK_CURRENCY_DECIMALS=18
NEXT_PUBLIC_NETWORK_CURRENCY_NAME=Ether
NEXT_PUBLIC_NETWORK_CURRENCY_SYMBOL=ETH
NEXT_PUBLIC_NETWORK_EXPLORERS=[{'title':'GeckoTerminal','logo':'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/explorer-logos/geckoterminal.png','baseUrl':'https://www.geckoterminal.com/','paths':{'token':'/optimism/pools'}}, {'title':'Tenderly','logo':'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/explorer-logos/tenderly.png','baseUrl':'https://dashboard.tenderly.co','paths':{'tx':'/tx/optimistic'}},{'title':'3xpl','logo':'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/explorer-logos/3xpl.png','baseUrl':'https://3xpl.com/','paths':{'tx':'/optimism/transaction','address':'/optimism/address'}}]
-NEXT_PUBLIC_NETWORK_ICON=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-icons/optimism.svg
-NEXT_PUBLIC_NETWORK_ICON_DARK=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-icons/optimism.svg
+NEXT_PUBLIC_NETWORK_ICON=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-icons/optimism-mainnet-light.svg
+NEXT_PUBLIC_NETWORK_ICON_DARK=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-icons/optimism-mainnet-dark.svg
NEXT_PUBLIC_NETWORK_ID=10
NEXT_PUBLIC_NETWORK_LOGO=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-logos/optimism.svg
NEXT_PUBLIC_NETWORK_LOGO_DARK=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-logos/optimism.svg
@@ -66,4 +70,5 @@ NEXT_PUBLIC_STATS_API_HOST=https://stats-optimism-mainnet.k8s-prod-1.blockscout.
NEXT_PUBLIC_TRANSACTION_INTERPRETATION_PROVIDER=blockscout
NEXT_PUBLIC_VIEWS_CONTRACT_SOLIDITYSCAN_ENABLED=true
NEXT_PUBLIC_VISUALIZE_API_HOST=https://visualizer.services.blockscout.com
-NEXT_PUBLIC_WEB3_WALLETS=['token_pocket', 'metamask']
\ No newline at end of file
+NEXT_PUBLIC_WEB3_WALLETS=['token_pocket', 'metamask']
+NEXT_PUBLIC_XSTAR_SCORE_URL=https://docs.xname.app/the-solution-adaptive-proof-of-humanity-on-blockchain/xhs-scoring-algorithm?utm_source=blockscout&utm_medium=address
\ No newline at end of file
diff --git a/configs/envs/.env.optimism_interop_0 b/configs/envs/.env.optimism_interop_0
new file mode 100644
index 0000000000..d424faa680
--- /dev/null
+++ b/configs/envs/.env.optimism_interop_0
@@ -0,0 +1,47 @@
+# Set of ENVs for OP Interop Alpha 0 network explorer
+# https://optimism-interop-alpha-0.blockscout.com
+# This is an auto-generated file. To update all values, run "yarn dev:preset:sync --name=optimism_interop_0"
+
+# Local ENVs
+NEXT_PUBLIC_APP_PROTOCOL=http
+NEXT_PUBLIC_APP_HOST=localhost
+NEXT_PUBLIC_APP_PORT=3000
+NEXT_PUBLIC_APP_ENV=development
+NEXT_PUBLIC_API_WEBSOCKET_PROTOCOL=ws
+
+# Instance ENVs
+NEXT_PUBLIC_AD_BANNER_PROVIDER=none
+NEXT_PUBLIC_AD_TEXT_PROVIDER=none
+NEXT_PUBLIC_API_BASE_PATH=/
+NEXT_PUBLIC_API_HOST=optimism-interop-alpha-0.blockscout.com
+NEXT_PUBLIC_API_SPEC_URL=https://raw.githubusercontent.com/blockscout/blockscout-api-v2-swagger/main/swagger.yaml
+NEXT_PUBLIC_CONTRACT_CODE_IDES=[{'title':'Remix IDE','url':'https://remix.ethereum.org/?address={hash}&blockscout={domain}','icon_url':'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/ide-icons/remix.png'}]
+NEXT_PUBLIC_FAULT_PROOF_ENABLED=true
+NEXT_PUBLIC_FOOTER_LINKS=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/footer-links/optimism.json
+NEXT_PUBLIC_GAME_BADGE_CLAIM_LINK=https://badges.blockscout.com/mint/sherblockHolmesBadge
+NEXT_PUBLIC_HOMEPAGE_CHARTS=['daily_txs']
+NEXT_PUBLIC_HOMEPAGE_HERO_BANNER_CONFIG={'background':['linear-gradient(90deg, rgb(232, 52, 53) 0%, rgb(139, 28, 232) 100%)'],'text_color':['rgb(255, 255, 255)']}
+NEXT_PUBLIC_INTEROP_ENABLED=true
+NEXT_PUBLIC_IS_TESTNET=true
+NEXT_PUBLIC_METADATA_SERVICE_API_HOST=https://metadata.services.blockscout.com
+NEXT_PUBLIC_NETWORK_CURRENCY_DECIMALS=18
+NEXT_PUBLIC_NETWORK_CURRENCY_NAME=Ether
+NEXT_PUBLIC_NETWORK_CURRENCY_SYMBOL=ETH
+NEXT_PUBLIC_NETWORK_ICON=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-icons/optimism.svg
+NEXT_PUBLIC_NETWORK_ICON_DARK=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-icons/optimism.svg
+NEXT_PUBLIC_NETWORK_ID=420120000
+NEXT_PUBLIC_NETWORK_LOGO=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-logos/optimism.svg
+NEXT_PUBLIC_NETWORK_LOGO_DARK=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-logos/optimism.svg
+NEXT_PUBLIC_NETWORK_NAME=OP Interop Alpha 0
+NEXT_PUBLIC_NETWORK_RPC_URL=https://interop-alpha-0.optimism.io
+NEXT_PUBLIC_NETWORK_SHORT_NAME=OP Interop Alpha 0
+NEXT_PUBLIC_OG_ENHANCED_DATA_ENABLED=true
+NEXT_PUBLIC_ROLLUP_L1_BASE_URL=https://eth-sepolia.blockscout.com/
+NEXT_PUBLIC_ROLLUP_L2_WITHDRAWAL_URL=https://bridge.interop-alpha-0.optimism.io
+NEXT_PUBLIC_ROLLUP_OUTPUT_ROOTS_ENABLED=false
+NEXT_PUBLIC_ROLLUP_TYPE=optimistic
+NEXT_PUBLIC_STATS_API_HOST=https://stats-optimism-interop-devnet-0.k8s-prod-1.blockscout.com
+NEXT_PUBLIC_TRANSACTION_INTERPRETATION_PROVIDER=blockscout
+NEXT_PUBLIC_VIEWS_TOKEN_SCAM_TOGGLE_ENABLED=true
+NEXT_PUBLIC_VISUALIZE_API_HOST=https://visualizer.services.blockscout.com
+NEXT_PUBLIC_WEB3_WALLETS=['token_pocket', 'metamask']
\ No newline at end of file
diff --git a/configs/envs/.env.scroll_sepolia b/configs/envs/.env.scroll_sepolia
index c29abc0442..43396566ab 100644
--- a/configs/envs/.env.scroll_sepolia
+++ b/configs/envs/.env.scroll_sepolia
@@ -14,7 +14,9 @@ NEXT_PUBLIC_API_BASE_PATH=/
NEXT_PUBLIC_API_HOST=scroll-sepolia.blockscout.com
NEXT_PUBLIC_API_SPEC_URL=https://raw.githubusercontent.com/blockscout/blockscout-api-v2-swagger/main/swagger.yaml
NEXT_PUBLIC_CONTRACT_CODE_IDES=[{'title':'Remix IDE','url':'https://remix.ethereum.org/?address={hash}&blockscout={domain}','icon_url':'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/ide-icons/remix.png'}]
+NEXT_PUBLIC_CONTRACT_INFO_API_HOST=https://contracts-info.services.blockscout.com
NEXT_PUBLIC_FEATURED_NETWORKS=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/featured-networks/scroll-testnet.json
+NEXT_PUBLIC_GAME_BADGE_CLAIM_LINK=https://badges.blockscout.com/mint/sherblockHolmesBadge
NEXT_PUBLIC_GRAPHIQL_TRANSACTION=0xa0d22caf6217a488b1e97b646c5ed88e8a3020a607bcd1f3fe8d4c430bb19ad5
NEXT_PUBLIC_HOMEPAGE_CHARTS=['daily_txs']
NEXT_PUBLIC_HOMEPAGE_HERO_BANNER_CONFIG={'background':['rgba(255, 238, 218, 1)'],'text_color':['rgba(25, 6, 2, 1)']}
@@ -33,9 +35,8 @@ NEXT_PUBLIC_NETWORK_RPC_URL=https://sepolia-rpc.scroll.io
NEXT_PUBLIC_NETWORK_SHORT_NAME=Scroll Sepolia Testnet
NEXT_PUBLIC_OG_ENHANCED_DATA_ENABLED=true
NEXT_PUBLIC_OG_IMAGE_URL=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/og-images/scroll-testnet.png
-NEXT_PUBLIC_RE_CAPTCHA_APP_SITE_KEY=6Ld0iT8aAAAAAJdju0CmAwGjW7JTDvIw-Q5pwt5T
+NEXT_PUBLIC_ROLLUP_L1_BASE_URL=https://eth-sepolia.blockscout.com
+NEXT_PUBLIC_ROLLUP_TYPE=scroll
NEXT_PUBLIC_STATS_API_HOST=https://stats-scroll-sepolia.k8s-prod-2.blockscout.com
NEXT_PUBLIC_TRANSACTION_INTERPRETATION_PROVIDER=blockscout
-NEXT_PUBLIC_VISUALIZE_API_HOST=https://visualizer.services.blockscout.com
-NEXT_PUBLIC_ROLLUP_TYPE=scroll
-NEXT_PUBLIC_ROLLUP_L1_BASE_URL=https://eth-sepolia.blockscout.com/
\ No newline at end of file
+NEXT_PUBLIC_VISUALIZE_API_HOST=https://visualizer.services.blockscout.com
\ No newline at end of file
diff --git a/decs.d.ts b/decs.d.ts
index 434d031e55..0f623b644b 100644
--- a/decs.d.ts
+++ b/decs.d.ts
@@ -1 +1,2 @@
-declare module 'react-identicons'
+declare module 'react-identicons';
+declare module 'use-font-face-observer';
diff --git a/deploy/helmfile.yaml b/deploy/helmfile.yaml
index 6052eda969..199073c4ad 100644
--- a/deploy/helmfile.yaml
+++ b/deploy/helmfile.yaml
@@ -8,8 +8,8 @@ helmDefaults:
recreatePods: false
repositories:
- - name: blockscout-ci-cd
- url: https://blockscout.github.io/blockscout-ci-cd
+ # - name: blockscout-ci-cd
+ # url: https://blockscout.github.io/blockscout-ci-cd
- name: blockscout
url: https://blockscout.github.io/helm-charts
- name: bedag
@@ -65,4 +65,4 @@ releases:
values:
- values/review/values.yaml.gotmpl
- global:
- env: "review"
\ No newline at end of file
+ env: "review"
diff --git a/deploy/scripts/build_sprite.sh b/deploy/scripts/build_sprite.sh
index 0db22fd14a..5eace17256 100755
--- a/deploy/scripts/build_sprite.sh
+++ b/deploy/scripts/build_sprite.sh
@@ -1,20 +1,70 @@
#!/bin/bash
-yarn icons build -i ./icons -o ./public/icons --optimize
+icons_dir="./icons"
+target_dir="./public/icons"
+
+yarn icons build -i $icons_dir -o $target_dir --optimize
+
+create_registry_file() {
+ # Create a temporary file to store the registry
+ local registry_file="$target_dir/registry.json"
+
+ # Start the JSON array
+ echo "[]" > "$registry_file"
+
+ # Detect OS and set appropriate stat command
+ get_file_size() {
+ local file="$1"
+ if [[ "$OSTYPE" == "darwin"* ]]; then
+ # macOS
+ stat -f%z "$file"
+ else
+ # Linux and others
+ stat -c%s "$file"
+ fi
+ }
+
+ # Function to process each file
+ process_file() {
+ local file="$1"
+ local relative_path="${file#$icons_dir/}"
+ local file_size=$(get_file_size "$file")
+
+ # Create a temporary file with the new entry
+ jq --arg name "$relative_path" --arg size "$file_size" \
+ '. + [{"name": $name, "file_size": ($size|tonumber)}]' \
+ "$registry_file" > "${registry_file}.tmp"
+
+ # Move the temporary file back
+ mv "${registry_file}.tmp" "$registry_file"
+ }
+
+ # Find all SVG files and process them
+ find "$icons_dir" -type f -name "*.svg" | while read -r file; do
+ process_file "$file"
+ done
+}
# Skip hash creation and renaming for playwright environment
if [ "$NEXT_PUBLIC_APP_ENV" != "pw" ]; then
# Generate hash from the sprite file
- HASH=$(md5sum ./public/icons/sprite.svg | cut -d' ' -f1 | head -c 8)
+ HASH=$(md5sum $target_dir/sprite.svg | cut -d' ' -f1 | head -c 8)
# Remove old sprite files
- rm -f ./public/icons/sprite.*.svg
+ rm -f $target_dir/sprite.*.svg
# Rename the new sprite file
- mv ./public/icons/sprite.svg "./public/icons/sprite.${HASH}.svg"
+ mv $target_dir/sprite.svg "$target_dir/sprite.${HASH}.svg"
export NEXT_PUBLIC_ICON_SPRITE_HASH=${HASH}
+ # Skip registry creation in development environment
+ # just to make the dev build faster
+ # remove this condition if you want to create the registry file in development environment
+ if [ "$NEXT_PUBLIC_APP_ENV" != "development" ]; then
+ create_registry_file
+ fi
+
echo "SVG sprite created: sprite.${HASH}.svg"
else
echo "SVG sprite created: sprite.svg (hash skipped for playwright environment)"
diff --git a/deploy/scripts/entrypoint.sh b/deploy/scripts/entrypoint.sh
index d4a7cd8134..6abcb1fa6c 100755
--- a/deploy/scripts/entrypoint.sh
+++ b/deploy/scripts/entrypoint.sh
@@ -35,9 +35,6 @@ export_envs_from_preset() {
# If there is a preset, load the environment variables from the its file
export_envs_from_preset
-# Generate OG image
-node --no-warnings ./og_image_generator.js
-
# Download external assets
./download_assets.sh ./public/assets/configs
@@ -61,6 +58,9 @@ else
fi
echo
+# Generate OG image
+node --no-warnings ./og_image_generator.js
+
# Create envs.js file with run-time environment variables for the client app
./make_envs_script.sh
@@ -71,4 +71,4 @@ echo
node ./feature-reporter.js
echo "Starting Next.js application"
-exec "$@"
\ No newline at end of file
+exec "$@"
diff --git a/deploy/scripts/og_image_generator.js b/deploy/scripts/og_image_generator.js
index b9b2edd2ba..6f607376ff 100755
--- a/deploy/scripts/og_image_generator.js
+++ b/deploy/scripts/og_image_generator.js
@@ -28,6 +28,7 @@ if (process.env.NEXT_PUBLIC_OG_IMAGE_URL) {
background: bannerConfig.background?.[0],
title_color: bannerConfig.text_color?.[0],
invert_logo: !process.env.NEXT_PUBLIC_NETWORK_LOGO_DARK,
+ app_url: process.env.NEXT_PUBLIC_APP_HOST,
};
console.log('⏳ Making request to OG image generator service...');
diff --git a/deploy/tools/envs-validator/schema.ts b/deploy/tools/envs-validator/schema.ts
index 2acf484528..7f1d60df7d 100644
--- a/deploy/tools/envs-validator/schema.ts
+++ b/deploy/tools/envs-validator/schema.ts
@@ -346,6 +346,17 @@ const rollupSchema = yup
value => value === undefined,
),
}),
+ NEXT_PUBLIC_INTEROP_ENABLED: yup
+ .boolean()
+ .when('NEXT_PUBLIC_ROLLUP_TYPE', {
+ is: 'optimistic',
+ then: (schema) => schema,
+ otherwise: (schema) => schema.test(
+ 'not-exist',
+ 'NEXT_PUBLIC_INTEROP_ENABLED can only be used if NEXT_PUBLIC_ROLLUP_TYPE is set to \'optimistic\' ',
+ value => value === undefined,
+ ),
+ }),
NEXT_PUBLIC_ROLLUP_PARENT_CHAIN_NAME: yup
.string()
.when('NEXT_PUBLIC_ROLLUP_TYPE', {
diff --git a/deploy/tools/envs-validator/test/.env.optimism b/deploy/tools/envs-validator/test/.env.optimism
index 8bd94ce0ea..32d36f3975 100644
--- a/deploy/tools/envs-validator/test/.env.optimism
+++ b/deploy/tools/envs-validator/test/.env.optimism
@@ -5,3 +5,4 @@ NEXT_PUBLIC_FAULT_PROOF_ENABLED=true
NEXT_PUBLIC_ROLLUP_HOMEPAGE_SHOW_LATEST_BLOCKS=true
NEXT_PUBLIC_ROLLUP_OUTPUT_ROOTS_ENABLED=false
NEXT_PUBLIC_ROLLUP_PARENT_CHAIN={'baseUrl':'https://explorer.duckchain.io'}
+NEXT_PUBLIC_INTEROP_ENABLED=true
\ No newline at end of file
diff --git a/deploy/tools/envs-validator/tsconfig.json b/deploy/tools/envs-validator/tsconfig.json
index a169faac5c..f7911a9e1a 100644
--- a/deploy/tools/envs-validator/tsconfig.json
+++ b/deploy/tools/envs-validator/tsconfig.json
@@ -4,6 +4,7 @@
"noEmit": false,
"target": "es2016",
"module": "CommonJS",
+ "moduleResolution": "node",
"paths": {
"nextjs-routes": ["./nextjs/nextjs-routes.d.ts"],
}
diff --git a/deploy/tools/feature-reporter/tsconfig.json b/deploy/tools/feature-reporter/tsconfig.json
index 1fefb24f4c..86b7068227 100644
--- a/deploy/tools/feature-reporter/tsconfig.json
+++ b/deploy/tools/feature-reporter/tsconfig.json
@@ -3,6 +3,7 @@
"compilerOptions": {
"noEmit": false,
"module": "CommonJS",
+ "moduleResolution": "node",
"outDir": "./build",
"paths": {
"nextjs-routes": ["./nextjs/nextjs-routes.d.ts"],
diff --git a/deploy/tools/sitemap-generator/next-sitemap.config.js b/deploy/tools/sitemap-generator/next-sitemap.config.js
index 98d55e9e11..a28468d42b 100644
--- a/deploy/tools/sitemap-generator/next-sitemap.config.js
+++ b/deploy/tools/sitemap-generator/next-sitemap.config.js
@@ -59,7 +59,7 @@ module.exports = {
{
userAgent: '*',
allow: '/',
- disallow: ['/auth/*', '/login', '/sprite', '/account/*', '/api/*', '/node-api/*'],
+ disallow: ['/auth/*', '/login', '/chakra', '/sprite', '/account/*', '/api/*', '/node-api/*'],
},
],
},
@@ -70,6 +70,7 @@ module.exports = {
'/auth/*',
'/login',
'/sprite',
+ '/chakra',
],
transform: async(config, path) => {
switch (path) {
@@ -114,6 +115,11 @@ module.exports = {
return null;
}
break;
+ case '/interop-messages':
+ if (process.env.NEXT_PUBLIC_INTEROP_ENABLED !== 'true') {
+ return null;
+ }
+ break;
case '/pools':
if (process.env.NEXT_PUBLIC_DEX_POOLS_ENABLED !== 'true') {
return null;
diff --git a/docs/ENVS.md b/docs/ENVS.md
index 0a89d60fa2..05fa5d75cc 100644
--- a/docs/ENVS.md
+++ b/docs/ENVS.md
@@ -2,11 +2,16 @@
The app instance can be customized by passing the following variables to the Node.js environment at runtime. Some of these variables have been deprecated, and their full list can be found in the [file](./DEPRECATED_ENVS.md).
-**IMPORTANT NOTE!** For _production_ build purposes all json-like values should be single-quoted. If it contains a hash (`#`) or a dollar-sign (`$`) the whole value should be wrapped in single quotes as well (see `dotenv` [readme](https://github.com/bkeepers/dotenv#variable-substitution) for the reference)
+## Read before you run the app
-## Disclaimer about using variables
+### Variables compulsoriness
+Please note that in the tables below, the "Compulsoriness" column indicates whether the variable is required for starting up the application, except for the "App Features" section. All features are optional by definition; therefore, the "Compulsoriness" column indicates whether a certain variable is required or optional only within the context of that feature, not for the entire application.
-Please be aware that all environment variables prefixed with `NEXT_PUBLIC_` will be exposed to the browser. So any user can obtain its values. Make sure that for all 3rd-party services keys (e.g., Sentri, Auth0, WalletConnect, etc.) in the services administration panel you have created a whitelist of allowed origins and have added your app domain into it. That will help you prevent using your key by unauthorized app, if someone gets its value.
+### Disclaimer about using variables
+Please be aware that all environment variables prefixed with `NEXT_PUBLIC_` will be exposed to the browser. So any user can obtain its values. Make sure that for all 3rd-party services keys (e.g., Auth0, WalletConnect, etc.) in the services administration panel you have created a whitelist of allowed origins and have added your app domain into it. That will help you prevent using your key by unauthorized app, if someone gets its value.
+
+### Note about escaping variables values
+All json-like values should be single-quoted. If it contains a hash (`#`) or a dollar-sign (`$`) the whole value should be wrapped in single quotes as well (see `dotenv` [readme](https://github.com/bkeepers/dotenv#variable-substitution) for the reference)
@@ -351,7 +356,7 @@ Settings for meta tags, OG tags and SEO
## App features
-*Note* The variables which are marked as required should be passed as described in order to enable the particular feature, but they are not required in the whole app context.
+*Note* The variables which are marked as required should be passed as described in order to enable the particular feature, but they are not required in the entire app context.
### My account
@@ -461,6 +466,7 @@ This feature is **enabled by default** with the `coinzilla` ads provider. To swi
| NEXT_PUBLIC_ROLLUP_L2_WITHDRAWAL_URL | `string` | URL for L2 -> L1 withdrawals (Optimistic stack only) | Required for `optimistic` rollups | - | `https://app.optimism.io/bridge/withdraw` | v1.24.0+ |
| NEXT_PUBLIC_FAULT_PROOF_ENABLED | `boolean` | Set to `true` for chains with fault proof system enabled (Optimistic stack only) | - | - | `true` | v1.31.0+ |
| NEXT_PUBLIC_HAS_MUD_FRAMEWORK | `boolean` | Set to `true` for instances that use MUD framework (Optimistic stack only) | - | - | `true` | v1.33.0+ |
+| NEXT_PUBLIC_INTEROP_ENABLED | `boolean` | Enables "Interop messages" page (Optimistic stack only) | - | `false` | `true` | v1.39.0+ |
| NEXT_PUBLIC_ROLLUP_HOMEPAGE_SHOW_LATEST_BLOCKS | `boolean` | Set to `true` to display "Latest blocks" widget instead of "Latest batches" on the home page | - | - | `true` | v1.36.0+ |
| NEXT_PUBLIC_ROLLUP_OUTPUT_ROOTS_ENABLED | `boolean` | Enables "Output roots" page (Optimistic stack only) | - | `false` | `true` | v1.37.0+ |
| NEXT_PUBLIC_ROLLUP_PARENT_CHAIN_NAME | `string` | Set to customize L1 transaction status labels in the UI (e.g., "Sent to "). This setting is applicable only for Arbitrum-based chains. **DEPRECATED** _Use `NEXT_PUBLIC_ROLLUP_PARENT_CHAIN` instead_ | - | - | `DuckChain` | v1.37.0+ |
diff --git a/eslint.config.mjs b/eslint.config.mjs
index 649a06c34f..8483f6bede 100644
--- a/eslint.config.mjs
+++ b/eslint.config.mjs
@@ -27,13 +27,24 @@ const RESTRICTED_MODULES = {
{ name: 'playwright/TestApp', message: 'Please use render() fixture from test() function of playwright/lib module' },
{
name: '@chakra-ui/react',
- importNames: [ 'Popover', 'Menu', 'PinInput', 'useToast', 'Skeleton' ],
- message: 'Please use corresponding component or hook from ui/shared/chakra component instead',
+ importNames: [
+ 'Menu', 'useToast', 'useDisclosure', 'useClipboard', 'Tooltip', 'Skeleton', 'IconButton', 'Button', 'ButtonGroup', 'Link', 'LinkBox', 'LinkOverlay',
+ 'Dialog', 'DialogRoot', 'DialogContent', 'DialogHeader', 'DialogCloseTrigger',
+ 'Tag', 'Switch', 'Image', 'Popover', 'PopoverTrigger', 'PopoverContent', 'PopoverBody', 'PopoverFooter',
+ 'DrawerRoot', 'DrawerBody', 'DrawerContent', 'DrawerOverlay', 'DrawerBackdrop', 'DrawerTrigger', 'Drawer',
+ 'Alert', 'AlertIcon', 'AlertTitle', 'AlertDescription',
+ 'Select', 'SelectRoot', 'SelectControl', 'SelectContent', 'SelectItem', 'SelectValueText',
+ 'Heading', 'Badge', 'Tabs', 'Show', 'Hide', 'Checkbox', 'CheckboxGroup',
+ 'Table', 'TableRoot', 'TableBody', 'TableHeader', 'TableRow', 'TableCell',
+ 'Menu', 'MenuRoot', 'MenuTrigger', 'MenuContent', 'MenuItem', 'MenuTriggerItem', 'MenuRadioItemGroup', 'MenuContextTrigger',
+ 'Rating', 'RatingGroup',
+ ],
+ message: 'Please use corresponding component or hook from "toolkit" instead',
},
{
name: 'next/link',
importNames: [ 'default' ],
- message: 'Please use ui/shared/NextLink component instead',
+ message: 'Please use toolkit/chakra/link component instead',
},
],
patterns: [
@@ -294,6 +305,7 @@ export default tseslint.config(
'/^playwright/',
'/^stubs/',
'/^theme/',
+ '/^toolkit/',
'/^ui/',
],
[ 'parent', 'sibling', 'index' ],
@@ -440,4 +452,13 @@ export default tseslint.config(
'no-restricted-properties': 'off',
},
},
+ {
+ files: [
+ 'toolkit/chakra/**',
+ ],
+ rules: {
+ // for toolkit components allow to import @chakra-ui/react directly
+ 'no-restricted-imports': 'off',
+ },
+ },
);
diff --git a/icons/close.svg b/icons/close.svg
new file mode 100644
index 0000000000..0631c7c76b
--- /dev/null
+++ b/icons/close.svg
@@ -0,0 +1,3 @@
+
+
+
diff --git a/icons/cross.svg b/icons/cross.svg
index 44a66fcc75..8bf9679845 100644
--- a/icons/cross.svg
+++ b/icons/cross.svg
@@ -1,3 +1,3 @@
-
-
+
+
diff --git a/icons/heart_filled.svg b/icons/heart_filled.svg
index 80926b1668..64635b353c 100644
--- a/icons/heart_filled.svg
+++ b/icons/heart_filled.svg
@@ -1,3 +1,3 @@
-
-
+
+
diff --git a/icons/heart_outline.svg b/icons/heart_outline.svg
index 8bf7ce3e36..34c36398f8 100644
--- a/icons/heart_outline.svg
+++ b/icons/heart_outline.svg
@@ -1,4 +1,4 @@
-
-
-
+
+
+
diff --git a/icons/info_filled.svg b/icons/info_filled.svg
new file mode 100644
index 0000000000..19bf3c0d31
--- /dev/null
+++ b/icons/info_filled.svg
@@ -0,0 +1,3 @@
+
+
+
diff --git a/icons/interop.svg b/icons/interop.svg
new file mode 100644
index 0000000000..b5692777ea
--- /dev/null
+++ b/icons/interop.svg
@@ -0,0 +1,3 @@
+
+
+
diff --git a/icons/search.svg b/icons/search.svg
index 1d7814fa05..c1849adc0d 100644
--- a/icons/search.svg
+++ b/icons/search.svg
@@ -1,3 +1,3 @@
-
+
diff --git a/icons/wallets/metamask.svg b/icons/wallets/metamask.svg
index ea6a6382a5..220fbc130f 100644
--- a/icons/wallets/metamask.svg
+++ b/icons/wallets/metamask.svg
@@ -1,14 +1,16 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/jest/lib.tsx b/jest/lib.tsx
index 061b375fe6..f6119698c7 100644
--- a/jest/lib.tsx
+++ b/jest/lib.tsx
@@ -1,4 +1,3 @@
-import { ChakraProvider } from '@chakra-ui/react';
import { GrowthBookProvider } from '@growthbook/growthbook-react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import type { RenderOptions } from '@testing-library/react';
@@ -8,7 +7,7 @@ import React from 'react';
import { AppContextProvider } from 'lib/contexts/app';
import { ScrollDirectionProvider } from 'lib/contexts/scrollDirection';
import { SocketProvider } from 'lib/socket/context';
-import theme from 'theme/theme';
+import { Provider as ChakraProvider } from 'toolkit/chakra/provider';
import 'lib/setLocale';
@@ -32,7 +31,7 @@ const TestApp = ({ children }: { children: React.ReactNode }) => {
}));
return (
-
+
diff --git a/jest/setup.ts b/jest/setup.ts
index 6ccc67ad4c..d64cff2d48 100644
--- a/jest/setup.ts
+++ b/jest/setup.ts
@@ -41,3 +41,15 @@ global.console = {
consoleError(...args);
},
};
+
+// Polyfill for structuredClone
+if (typeof structuredClone === 'undefined') {
+ global.structuredClone = (obj: T): T => {
+ try {
+ return JSON.parse(JSON.stringify(obj)) as T;
+ } catch (error) {
+ // Fallback for circular references and other special cases
+ return obj;
+ }
+ };
+}
diff --git a/lib/address/getCheckedSummedAddress.ts b/lib/address/getCheckedSummedAddress.ts
index 5c92b9a390..c3779744f8 100644
--- a/lib/address/getCheckedSummedAddress.ts
+++ b/lib/address/getCheckedSummedAddress.ts
@@ -2,12 +2,18 @@ import { getAddress } from 'viem';
import config from 'configs/app';
+const ERC1191_CHAIN_IDS = [
+ '30', // RSK Mainnet
+ '31', // RSK Testnet
+];
+
export default function getCheckedSummedAddress(address: string): string {
try {
return getAddress(
address,
- // We need to pass chainId to getAddress to make it work correctly for some chains, e.g. Rootstock
- config.chain.id ? Number(config.chain.id) : undefined,
+ // We need to pass chainId to getAddress to make it work correctly for chains that support ERC-1191
+ // https://eips.ethereum.org/EIPS/eip-1191#usage--table
+ ERC1191_CHAIN_IDS.includes(config.chain.id ?? '') ? Number(config.chain.id) : undefined,
);
} catch (error) {
return address;
diff --git a/lib/api/resources.ts b/lib/api/resources.ts
index e23a78f936..c593b2f17b 100644
--- a/lib/api/resources.ts
+++ b/lib/api/resources.ts
@@ -82,6 +82,7 @@ import type {
} from 'types/api/ens';
import type { IndexingStatus } from 'types/api/indexingStatus';
import type { InternalTransactionsResponse } from 'types/api/internalTransaction';
+import type { InteropMessageListResponse } from 'types/api/interop';
import type { LogsResponseTx, LogsResponseAddress } from 'types/api/log';
import type { MudWorldsResponse } from 'types/api/mudWorlds';
import type { NovesAccountHistoryResponse, NovesDescribeTxsResponse, NovesResponseData } from 'types/api/noves';
@@ -1243,6 +1244,15 @@ export const RESOURCES = {
block_countdown: {
path: '/api',
},
+
+ // INTEROP
+ optimistic_l2_interop_messages: {
+ path: '/api/v2/optimism/interop/messages',
+ filterFields: [],
+ },
+ optimistic_l2_interop_messages_count: {
+ path: '/api/v2/optimism/interop/messages/count',
+ },
};
export type ResourceName = keyof typeof RESOURCES;
@@ -1296,7 +1306,7 @@ export type PaginatedResources = 'blocks' | 'block_txs' | 'block_election_reward
'watchlist' | 'private_tags_address' | 'private_tags_tx' |
'domains_lookup' | 'addresses_lookup' | 'user_ops' | 'validators_stability' | 'validators_blackfort' | 'validators_zilliqa' | 'noves_address_history' |
'token_transfers_all' | 'scroll_l2_txn_batches' | 'scroll_l2_txn_batch_txs' | 'scroll_l2_txn_batch_blocks' |
-'scroll_l2_deposits' | 'scroll_l2_withdrawals' | 'advanced_filter' | 'pools';
+'scroll_l2_deposits' | 'scroll_l2_withdrawals' | 'advanced_filter' | 'pools' | 'optimistic_l2_interop_messages';
export type PaginatedResponse = ResourcePayload;
@@ -1473,6 +1483,8 @@ Q extends 'optimistic_l2_output_roots_count' ? number :
Q extends 'optimistic_l2_withdrawals_count' ? number :
Q extends 'optimistic_l2_deposits_count' ? number :
Q extends 'optimistic_l2_dispute_games_count' ? number :
+Q extends 'optimistic_l2_interop_messages' ? InteropMessageListResponse :
+Q extends 'optimistic_l2_interop_messages_count' ? number :
Q extends 'shibarium_withdrawals' ? ShibariumWithdrawalsResponse :
Q extends 'shibarium_deposits' ? ShibariumDepositsResponse :
Q extends 'shibarium_withdrawals_count' ? number :
diff --git a/lib/contexts/chakra.tsx b/lib/contexts/chakra.tsx
deleted file mode 100644
index 3ace6cd278..0000000000
--- a/lib/contexts/chakra.tsx
+++ /dev/null
@@ -1,30 +0,0 @@
-import {
- ChakraProvider as ChakraProviderDefault,
- cookieStorageManagerSSR,
- localStorageManager,
-} from '@chakra-ui/react';
-import type { ChakraProviderProps } from '@chakra-ui/react';
-import React from 'react';
-
-import { get, NAMES } from 'lib/cookies';
-import theme from 'theme/theme';
-
-interface Props extends ChakraProviderProps {
- cookies?: string;
-}
-
-export function ChakraProvider({ cookies, children }: Props) {
- // When a cookie header is present, cookieStorageManagerSSR looks for a "chakra-ui-color-mode" cookie.
- // If it doesn’t find one, it doesn’t consider theme’s initialColorMode. Instead, it is defaulting to light mode
- // So we need to use localStorageManager instead of cookieStorageManagerSSR to get the correct default color mode
- const colorModeManager =
- typeof cookies === 'string' && get(NAMES.COLOR_MODE, cookies) ?
- cookieStorageManagerSSR(typeof document !== 'undefined' ? document.cookie : cookies) :
- localStorageManager;
-
- return (
-
- { children }
-
- );
-}
diff --git a/lib/contexts/rewards.tsx b/lib/contexts/rewards.tsx
index b74aa95f1b..672bfebce9 100644
--- a/lib/contexts/rewards.tsx
+++ b/lib/contexts/rewards.tsx
@@ -1,6 +1,6 @@
-import { useBoolean } from '@chakra-ui/react';
import type { UseQueryResult } from '@tanstack/react-query';
import { useQueryClient } from '@tanstack/react-query';
+import { useToggle } from '@uidotdev/usehooks';
import { useRouter } from 'next/router';
import React, { createContext, useContext, useEffect, useMemo, useCallback } from 'react';
import { useSignMessage } from 'wagmi';
@@ -22,10 +22,10 @@ import * as cookies from 'lib/cookies';
import decodeJWT from 'lib/decodeJWT';
import getErrorMessage from 'lib/errors/getErrorMessage';
import getErrorObjPayload from 'lib/errors/getErrorObjPayload';
-import useToast from 'lib/hooks/useToast';
import getQueryParamString from 'lib/router/getQueryParamString';
import removeQueryParam from 'lib/router/removeQueryParam';
import useAccount from 'lib/web3/useAccount';
+import { toaster } from 'toolkit/chakra/toaster';
import useProfileQuery from 'ui/snippets/auth/useProfileQuery';
const feature = config.features.rewards;
@@ -45,7 +45,7 @@ type TRewardsContext = {
openLoginModal: () => void;
closeLoginModal: () => void;
saveApiToken: (token: string | undefined) => void;
- login: (refCode: string) => Promise<{ isNewUser?: boolean; invalidRefCodeError?: boolean }>;
+ login: (refCode: string) => Promise<{ isNewUser: boolean; reward: string | null; invalidRefCodeError?: boolean }>;
claim: () => Promise;
};
@@ -70,7 +70,7 @@ const initialState = {
openLoginModal: () => {},
closeLoginModal: () => {},
saveApiToken: () => {},
- login: async() => ({}),
+ login: async() => ({ isNewUser: false, reward: null }),
claim: async() => {},
};
@@ -116,13 +116,12 @@ export function RewardsContextProvider({ children }: Props) {
const router = useRouter();
const queryClient = useQueryClient();
const apiFetch = useApiFetch();
- const toast = useToast();
const { address } = useAccount();
const { signMessageAsync } = useSignMessage();
const profileQuery = useProfileQuery();
- const [ isLoginModalOpen, setIsLoginModalOpen ] = useBoolean(false);
- const [ isInitialized, setIsInitialized ] = useBoolean(false);
+ const [ isLoginModalOpen, setIsLoginModalOpen ] = useToggle(false);
+ const [ isInitialized, setIsInitialized ] = useToggle(false);
const [ apiToken, setApiToken ] = React.useState();
// Initialize state with the API token from cookies
@@ -133,7 +132,7 @@ export function RewardsContextProvider({ children }: Props) {
if (registeredAddress === profileQuery.data?.address_hash) {
setApiToken(token);
}
- setIsInitialized.on();
+ setIsInitialized(true);
}
}, [ setIsInitialized, profileQuery ]);
@@ -189,22 +188,18 @@ export function RewardsContextProvider({ children }: Props) {
cookies.set(cookies.NAMES.REWARDS_REFERRAL_CODE, refCode);
removeQueryParam(router, 'ref');
if (!apiToken) {
- setIsLoginModalOpen.on();
+ setIsLoginModalOpen(true);
}
}
}, [ router, apiToken, isInitialized, setIsLoginModalOpen ]);
const errorToast = useCallback((error: unknown) => {
const apiError = getErrorObjPayload<{ message: string }>(error);
- toast({
- position: 'top-right',
+ toaster.error({
title: 'Error',
description: apiError?.message || getErrorMessage(error) || 'Something went wrong. Try again later.',
- status: 'error',
- variant: 'subtle',
- isClosable: true,
});
- }, [ toast ]);
+ }, [ ]);
// Login to the rewards program
const login = useCallback(async(refCode: string) => {
@@ -216,10 +211,14 @@ export function RewardsContextProvider({ children }: Props) {
apiFetch('rewards_nonce') as Promise,
refCode ?
apiFetch('rewards_check_ref_code', { pathParams: { code: refCode } }) as Promise :
- Promise.resolve({ valid: true }),
+ Promise.resolve({ valid: true, reward: null }),
]);
if (!checkCodeResponse.valid) {
- return { invalidRefCodeError: true };
+ return {
+ invalidRefCodeError: true,
+ isNewUser: false,
+ reward: null,
+ };
}
const message = getMessageToSign(address, nonceResponse.nonce, checkUserQuery.data?.exists, refCode);
const signature = await signMessageAsync({ message });
@@ -234,7 +233,10 @@ export function RewardsContextProvider({ children }: Props) {
},
}) as RewardsLoginResponse;
saveApiToken(loginResponse.token);
- return { isNewUser: loginResponse.created };
+ return {
+ isNewUser: loginResponse.created,
+ reward: checkCodeResponse.reward,
+ };
} catch (_error) {
errorToast(_error);
throw _error;
@@ -256,6 +258,14 @@ export function RewardsContextProvider({ children }: Props) {
}
}, [ apiFetch, errorToast, fetchParams ]);
+ const openLoginModal = React.useCallback(() => {
+ setIsLoginModalOpen(true);
+ }, [ setIsLoginModalOpen ]);
+
+ const closeLoginModal = React.useCallback(() => {
+ setIsLoginModalOpen(false);
+ }, [ setIsLoginModalOpen ]);
+
const value = useMemo(() => {
if (!feature.isEnabled) {
return initialState;
@@ -270,14 +280,15 @@ export function RewardsContextProvider({ children }: Props) {
saveApiToken,
isInitialized,
isLoginModalOpen,
- openLoginModal: setIsLoginModalOpen.on,
- closeLoginModal: setIsLoginModalOpen.off,
+ openLoginModal,
+ closeLoginModal,
login,
claim,
};
}, [
- isLoginModalOpen, setIsLoginModalOpen, balancesQuery, dailyRewardQuery, checkUserQuery,
+ balancesQuery, dailyRewardQuery, checkUserQuery,
apiToken, login, claim, referralsQuery, rewardsConfigQuery, isInitialized, saveApiToken,
+ isLoginModalOpen, openLoginModal, closeLoginModal,
]);
return (
diff --git a/lib/hooks/useGraphLinks.tsx b/lib/hooks/useGraphLinks.tsx
index b8f83b4cf8..8943f38bae 100644
--- a/lib/hooks/useGraphLinks.tsx
+++ b/lib/hooks/useGraphLinks.tsx
@@ -9,7 +9,7 @@ const feature = config.features.marketplace;
export default function useGraphLinks() {
const fetch = useFetch();
- return useQuery, Record>>({
+ return useQuery, Record>>({
queryKey: [ 'graph-links' ],
queryFn: async() => fetch((feature.isEnabled && feature.graphLinksUrl) ? feature.graphLinksUrl : '', undefined, { resource: 'graph-links' }),
enabled: feature.isEnabled && Boolean(feature.graphLinksUrl),
diff --git a/lib/hooks/useInitialList.tsx b/lib/hooks/useInitialList.tsx
new file mode 100644
index 0000000000..e8f5aab9a3
--- /dev/null
+++ b/lib/hooks/useInitialList.tsx
@@ -0,0 +1,36 @@
+import React from 'react';
+
+type Id = string | number;
+
+export interface Params {
+ data: Array;
+ idFn: (item: T) => Id;
+ enabled: boolean;
+}
+
+export default function useInitialList({ data, idFn, enabled }: Params) {
+ const [ list, setList ] = React.useState>([]);
+
+ React.useEffect(() => {
+ if (enabled) {
+ setList(data.map(idFn));
+ }
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [ enabled ]);
+
+ const isNew = React.useCallback((data: T) => {
+ return !list.includes(idFn(data));
+ }, [ list, idFn ]);
+
+ const getAnimationProp = React.useCallback((data: T) => {
+ return isNew(data) ? 'fade-in 500ms linear' : undefined;
+ }, [ isNew ]);
+
+ return React.useMemo(() => {
+ return {
+ list,
+ isNew,
+ getAnimationProp,
+ };
+ }, [ list, isNew, getAnimationProp ]);
+}
diff --git a/lib/hooks/useNavItems.tsx b/lib/hooks/useNavItems.tsx
index 28c130c713..fda83f31b8 100644
--- a/lib/hooks/useNavItems.tsx
+++ b/lib/hooks/useNavItems.tsx
@@ -115,6 +115,13 @@ export default function useNavItems(): ReturnType {
const rollupFeature = config.features.rollup;
+ const rollupInteropMessages = rollupFeature.isEnabled && rollupFeature.interopEnabled ? {
+ text: 'Interop messages',
+ nextRoute: { pathname: '/interop-messages' as const },
+ icon: 'interop',
+ isActive: pathname === '/interop-messages',
+ } : null;
+
if (rollupFeature.isEnabled && (
rollupFeature.type === 'optimistic' ||
rollupFeature.type === 'arbitrum' ||
@@ -127,7 +134,8 @@ export default function useNavItems(): ReturnType {
internalTxs,
rollupDeposits,
rollupWithdrawals,
- ],
+ rollupInteropMessages,
+ ].filter(Boolean),
[
blocks,
rollupTxnBatches,
diff --git a/lib/hooks/useToast.tsx b/lib/hooks/useToast.tsx
deleted file mode 100644
index 7afc6f3e11..0000000000
--- a/lib/hooks/useToast.tsx
+++ /dev/null
@@ -1,28 +0,0 @@
-import type { UseToastOptions, ToastProps } from '@chakra-ui/react';
-import { createToastFn, useChakra } from '@chakra-ui/react';
-import React from 'react';
-
-import Toast from 'ui/shared/chakra/Toast';
-
-// there is no toastComponent prop in UseToastOptions type
-// but these options will be passed to createRenderToast under the hood
-// and it accepts custom toastComponent
-const defaultOptions: UseToastOptions & { toastComponent?: React.FC } = {
- toastComponent: Toast,
- position: 'top-right',
- isClosable: true,
- containerStyle: {
- margin: 3,
- marginBottom: 0,
- },
- variant: 'subtle',
-};
-
-export default function useToastModified() {
- const { theme } = useChakra();
-
- return React.useMemo(
- () => createToastFn(theme.direction, defaultOptions),
- [ theme.direction ],
- );
-}
diff --git a/lib/metadata/getPageOgType.ts b/lib/metadata/getPageOgType.ts
index 8f26075149..70a299cf99 100644
--- a/lib/metadata/getPageOgType.ts
+++ b/lib/metadata/getPageOgType.ts
@@ -60,10 +60,12 @@ const OG_TYPE_DICT: Record = {
'/advanced-filter': 'Root page',
'/pools': 'Root page',
'/pools/[hash]': 'Regular page',
+ '/interop-messages': 'Root page',
// service routes, added only to make typescript happy
'/login': 'Regular page',
'/sprite': 'Regular page',
+ '/chakra': 'Regular page',
'/api/metrics': 'Regular page',
'/api/monitoring/invalid-api-schema': 'Regular page',
'/api/log': 'Regular page',
@@ -72,7 +74,6 @@ const OG_TYPE_DICT: Record = {
'/api/csrf': 'Regular page',
'/api/healthz': 'Regular page',
'/api/config': 'Regular page',
- '/api/sprite': 'Regular page',
};
export default function getPageOgType(pathname: Route['pathname']) {
diff --git a/lib/metadata/templates/description.ts b/lib/metadata/templates/description.ts
index 6c738d5bf2..b90e9e6fe1 100644
--- a/lib/metadata/templates/description.ts
+++ b/lib/metadata/templates/description.ts
@@ -63,10 +63,12 @@ const TEMPLATE_MAP: Record = {
'/advanced-filter': DEFAULT_TEMPLATE,
'/pools': DEFAULT_TEMPLATE,
'/pools/[hash]': DEFAULT_TEMPLATE,
+ '/interop-messages': DEFAULT_TEMPLATE,
// service routes, added only to make typescript happy
'/login': DEFAULT_TEMPLATE,
'/sprite': DEFAULT_TEMPLATE,
+ '/chakra': DEFAULT_TEMPLATE,
'/api/metrics': DEFAULT_TEMPLATE,
'/api/monitoring/invalid-api-schema': DEFAULT_TEMPLATE,
'/api/log': DEFAULT_TEMPLATE,
@@ -75,7 +77,6 @@ const TEMPLATE_MAP: Record = {
'/api/csrf': DEFAULT_TEMPLATE,
'/api/healthz': DEFAULT_TEMPLATE,
'/api/config': DEFAULT_TEMPLATE,
- '/api/sprite': DEFAULT_TEMPLATE,
};
const TEMPLATE_MAP_ENHANCED: Partial> = {
diff --git a/lib/metadata/templates/title.ts b/lib/metadata/templates/title.ts
index 9c3f62ec89..947fc5cbaf 100644
--- a/lib/metadata/templates/title.ts
+++ b/lib/metadata/templates/title.ts
@@ -60,10 +60,12 @@ const TEMPLATE_MAP: Record = {
'/advanced-filter': '%network_name% advanced filter',
'/pools': '%network_name% DEX pools',
'/pools/[hash]': '%network_name% pool details',
+ '/interop-messages': '%network_name% interop messages',
// service routes, added only to make typescript happy
'/login': '%network_name% login',
'/sprite': '%network_name% SVG sprite',
+ '/chakra': '%network_name% Chakra UI showcase',
'/api/metrics': '%network_name% node API prometheus metrics',
'/api/monitoring/invalid-api-schema': '%network_name% node API prometheus metrics',
'/api/log': '%network_name% node API request log',
@@ -72,7 +74,6 @@ const TEMPLATE_MAP: Record = {
'/api/csrf': '%network_name% node API CSRF token',
'/api/healthz': '%network_name% node API health check',
'/api/config': '%network_name% node API app config',
- '/api/sprite': '%network_name% node API SVG sprite content',
};
const TEMPLATE_MAP_ENHANCED: Partial> = {
diff --git a/lib/mixpanel/getPageType.ts b/lib/mixpanel/getPageType.ts
index 12ae0eb2cb..4303c2d111 100644
--- a/lib/mixpanel/getPageType.ts
+++ b/lib/mixpanel/getPageType.ts
@@ -58,10 +58,12 @@ export const PAGE_TYPE_DICT: Record = {
'/advanced-filter': 'Advanced filter',
'/pools': 'DEX pools',
'/pools/[hash]': 'Pool details',
+ '/interop-messages': 'Interop messages',
// service routes, added only to make typescript happy
'/login': 'Login',
'/sprite': 'Sprite',
+ '/chakra': 'Chakra UI showcase',
'/api/metrics': 'Node API: Prometheus metrics',
'/api/monitoring/invalid-api-schema': 'Node API: Prometheus metrics',
'/api/log': 'Node API: Request log',
@@ -70,7 +72,6 @@ export const PAGE_TYPE_DICT: Record = {
'/api/csrf': 'Node API: CSRF token',
'/api/healthz': 'Node API: Health check',
'/api/config': 'Node API: App config',
- '/api/sprite': 'Node API: SVG sprite content',
};
export default function getPageType(pathname: Route['pathname']) {
diff --git a/lib/mixpanel/useLogPageView.tsx b/lib/mixpanel/useLogPageView.tsx
index da5a6952f0..b7a09a6d61 100644
--- a/lib/mixpanel/useLogPageView.tsx
+++ b/lib/mixpanel/useLogPageView.tsx
@@ -1,5 +1,3 @@
-import type { ColorMode } from '@chakra-ui/react';
-import { useColorMode } from '@chakra-ui/react';
import { usePathname } from 'next/navigation';
import { useRouter } from 'next/router';
import React from 'react';
@@ -8,6 +6,8 @@ import config from 'configs/app';
import * as cookies from 'lib/cookies';
import getQueryParamString from 'lib/router/getQueryParamString';
import { COLOR_THEMES } from 'lib/settings/colorTheme';
+import { useColorMode } from 'toolkit/chakra/color-mode';
+import type { ColorMode } from 'toolkit/chakra/color-mode';
import getPageType from './getPageType';
import getTabName from './getTabName';
diff --git a/lib/settings/colorTheme.ts b/lib/settings/colorTheme.ts
index c5b7edaa1f..5745ea693d 100644
--- a/lib/settings/colorTheme.ts
+++ b/lib/settings/colorTheme.ts
@@ -1,7 +1,7 @@
-import type { ColorMode } from '@chakra-ui/react';
-
import type { ColorThemeId } from 'types/settings';
+import type { ColorMode } from 'toolkit/chakra/color-mode';
+
interface ColorTheme {
id: ColorThemeId;
label: string;
diff --git a/lib/socket/types.ts b/lib/socket/types.ts
index 11ae7db150..5674dd1449 100644
--- a/lib/socket/types.ts
+++ b/lib/socket/types.ts
@@ -49,13 +49,13 @@ interface SocketMessageParamsGeneric;
- export type BlocksIndexStatus = SocketMessageParamsGeneric<'block_index_status', { finished: boolean; ratio: string }>;
- export type InternalTxsIndexStatus = SocketMessageParamsGeneric<'internal_txs_index_status', { finished: boolean; ratio: string }>;
+ export type BlocksIndexStatus = SocketMessageParamsGeneric<'index_status', { finished: boolean; ratio: string }>;
+ export type InternalTxsIndexStatus = SocketMessageParamsGeneric<'index_status', { finished: boolean; ratio: string }>;
export type TxStatusUpdate = SocketMessageParamsGeneric<'collated', NewBlockSocketResponse>;
export type TxRawTrace = SocketMessageParamsGeneric<'raw_trace', RawTracesResponse>;
export type NewTx = SocketMessageParamsGeneric<'transaction', { transaction: number }>;
export type NewPendingTx = SocketMessageParamsGeneric<'pending_transaction', { pending_transaction: number }>;
- export type NewOptimisticDeposits = SocketMessageParamsGeneric<'deposits', { deposits: number }>;
+ export type NewOptimisticDeposits = SocketMessageParamsGeneric<'new_optimism_deposits', { deposits: number }>;
export type NewArbitrumDeposits = SocketMessageParamsGeneric<'new_messages_to_rollup_amount', { new_messages_to_rollup_amount: number }>;
export type AddressBalance = SocketMessageParamsGeneric<'balance', { balance: string; block_number: number; exchange_rate: string }>;
export type AddressCurrentCoinBalance =
diff --git a/lib/socket/useSocketChannel.tsx b/lib/socket/useSocketChannel.tsx
index f0a67bc9b4..17f9ec3b0d 100644
--- a/lib/socket/useSocketChannel.tsx
+++ b/lib/socket/useSocketChannel.tsx
@@ -3,7 +3,7 @@ import { useEffect, useRef, useState } from 'react';
import { useSocket } from './context';
-const CHANNEL_REGISTRY: Record = {};
+const CHANNEL_REGISTRY: Record = {};
interface Params {
topic: string | undefined;
@@ -53,11 +53,12 @@ export default function useSocketChannel({ topic, params, isDisabled, onJoin, on
let ch: Channel;
if (CHANNEL_REGISTRY[topic]) {
- ch = CHANNEL_REGISTRY[topic];
+ ch = CHANNEL_REGISTRY[topic].channel;
+ CHANNEL_REGISTRY[topic].subscribers++;
onJoinRef.current?.(ch, '');
} else {
ch = socket.channel(topic);
- CHANNEL_REGISTRY[topic] = ch;
+ CHANNEL_REGISTRY[topic] = { channel: ch, subscribers: 1 };
ch.join()
.receive('ok', (message) => onJoinRef.current?.(ch, message))
.receive('error', () => {
@@ -68,8 +69,14 @@ export default function useSocketChannel({ topic, params, isDisabled, onJoin, on
setChannel(ch);
return () => {
- ch.leave();
- delete CHANNEL_REGISTRY[topic];
+ if (CHANNEL_REGISTRY[topic]) {
+ CHANNEL_REGISTRY[topic].subscribers > 0 && CHANNEL_REGISTRY[topic].subscribers--;
+ if (CHANNEL_REGISTRY[topic].subscribers === 0) {
+ ch.leave();
+ delete CHANNEL_REGISTRY[topic];
+ }
+ }
+
setChannel(undefined);
};
}, [ socket, topic, params, isDisabled, onSocketError ]);
diff --git a/mocks/interop/interop.ts b/mocks/interop/interop.ts
new file mode 100644
index 0000000000..ca91595ad0
--- /dev/null
+++ b/mocks/interop/interop.ts
@@ -0,0 +1,56 @@
+import type { ChainInfo, InteropMessage } from 'types/api/interop';
+
+export const chain: ChainInfo = {
+ chain_id: 1,
+ chain_name: 'Ethereum',
+ chain_logo: 'https://example.com/logo.png',
+ instance_url: 'https://example.com',
+};
+
+export const interopMessageIn: InteropMessage = {
+ init_transaction_hash: '0x047A81aFB05D9B1f8844bf60fcA05DCCFbC584B9',
+ nonce: 1,
+ payload: 'payload',
+ relay_transaction_hash: '0x62d597ebcf3e8d60096dd0363bc2f0f5e2df27ba1dacd696c51aa7c9409f3193',
+ sender: '0x047A81aFB05D9B1f8844bf60fcA05DCCFbC584B9',
+ status: 'Relayed',
+ target: '0x047A81aFB05D9B1f8844bf60fcA05DCCFbC584B9',
+ timestamp: '2022-10-10T14:34:30.000000Z',
+ init_chain: chain,
+};
+
+export const interopMessageIn1: InteropMessage = {
+ init_transaction_hash: '0x047A81aFB05D9B1f8844bf60fcA05DCCFbC584B9',
+ nonce: 1,
+ payload: 'payload',
+ relay_transaction_hash: '0x62d597ebcf3e8d60096dd0363bc2f0f5e2df27ba1dacd696c51aa7c9409f3193',
+ sender: '0x047A81aFB05D9B1f8844bf60fcA05DCCFbC584B9',
+ status: 'Sent',
+ target: '0x047A81aFB05D9B1f8844bf60fcA05DCCFbC584B9',
+ timestamp: '2022-10-10T14:34:30.000000Z',
+ init_chain: null,
+};
+
+export const interopMessageOut: InteropMessage = {
+ init_transaction_hash: '0x047A81aFB05D9B1f8844bf60fcA05DCCFbC584B9',
+ nonce: 1,
+ payload: 'payload',
+ relay_transaction_hash: '0x62d597ebcf3e8d60096dd0363bc2f0f5e2df27ba1dacd696c51aa7c9409f3193',
+ sender: '0x047A81aFB05D9B1f8844bf60fcA05DCCFbC584B9',
+ status: 'Relayed',
+ target: '0x047A81aFB05D9B1f8844bf60fcA05DCCFbC584B9',
+ timestamp: '2022-10-10T14:34:30.000000Z',
+ relay_chain: chain,
+};
+
+export const interopMessageOut1: InteropMessage = {
+ init_transaction_hash: '0x047A81aFB05D9B1f8844bf60fcA05DCCFbC584B9',
+ nonce: 1,
+ payload: 'payload',
+ relay_transaction_hash: '0x62d597ebcf3e8d60096dd0363bc2f0f5e2df27ba1dacd696c51aa7c9409f3193',
+ sender: '0x047A81aFB05D9B1f8844bf60fcA05DCCFbC584B9',
+ status: 'Failed',
+ target: '0x047A81aFB05D9B1f8844bf60fcA05DCCFbC584B9',
+ timestamp: '2022-10-10T14:34:30.000000Z',
+ relay_chain: null,
+};
diff --git a/mocks/tokens/tokenTransfer.ts b/mocks/tokens/tokenTransfer.ts
index f17da56ec4..d09044ab31 100644
--- a/mocks/tokens/tokenTransfer.ts
+++ b/mocks/tokens/tokenTransfer.ts
@@ -1,6 +1,8 @@
import type { TokenInfo } from 'types/api/token';
import type { TokenTransfer, TokenTransferResponse } from 'types/api/tokenTransfer';
+import * as tokenInstanceMock from './tokenInstance';
+
export const erc20: TokenTransfer = {
from: {
hash: '0xd789a607CEac2f0E14867de4EB15b15C9FFB5859',
@@ -86,6 +88,7 @@ export const erc721: TokenTransfer = {
},
total: {
token_id: '875879856',
+ token_instance: tokenInstanceMock.base,
},
transaction_hash: '0xf13bc7afe5e02b494dd2f22078381d36a4800ef94a0ccc147431db56c301e6cc',
type: 'token_transfer',
@@ -135,6 +138,7 @@ export const erc1155A: TokenTransfer = {
token_id: '123',
value: '42',
decimals: null,
+ token_instance: null,
},
transaction_hash: '0x05d6589367633c032d757a69c5fb16c0e33e3994b0d9d1483f82aeee1f05d746',
type: 'token_minting',
@@ -151,7 +155,7 @@ export const erc1155B: TokenTransfer = {
name: 'SastanaNFT',
symbol: 'ipfs://QmUpFUfVKDCWeZQk5pvDFUxnpQP9N6eLSHhNUy49T1JVtY',
},
- total: { token_id: '12345678', value: '100000000000000000000', decimals: null },
+ total: { token_id: '12345678', value: '100000000000000000000', decimals: null, token_instance: null },
};
export const erc1155C: TokenTransfer = {
@@ -161,7 +165,7 @@ export const erc1155C: TokenTransfer = {
name: 'SastanaNFT',
symbol: 'ipfs://QmUpFUfVKDCWeZQk5pvDFUxnpQP9N6eLSHhNUy49T1JVtY',
},
- total: { token_id: '483200961027732618117991942553110860267520', value: '200000000000000000000', decimals: null },
+ total: { token_id: '483200961027732618117991942553110860267520', value: '200000000000000000000', decimals: null, token_instance: null },
};
export const erc1155D: TokenTransfer = {
@@ -171,7 +175,7 @@ export const erc1155D: TokenTransfer = {
name: 'SastanaNFT',
symbol: 'ipfs://QmUpFUfVKDCWeZQk5pvDFUxnpQP9N6eLSHhNUy49T1JVtY',
},
- total: { token_id: '456', value: '42', decimals: null },
+ total: { token_id: '456', value: '42', decimals: null, token_instance: null },
};
export const erc404A: TokenTransfer = {
@@ -213,6 +217,7 @@ export const erc404A: TokenTransfer = {
value: '42000000000000000000000000',
decimals: '18',
token_id: null,
+ token_instance: null,
},
transaction_hash: '0x05d6589367633c032d757a69c5fb16c0e33e3994b0d9d1483f82aeee1f05d746',
type: 'token_transfer',
@@ -230,7 +235,7 @@ export const erc404B: TokenTransfer = {
name: 'SastanaNFT',
symbol: 'ipfs://QmUpFUfVKDCWeZQk5pvDFUxnpQP9N6eLSHhNUy49T1JVtY',
},
- total: { token_id: '4625304364899952' },
+ total: { token_id: '4625304364899952', token_instance: null },
};
export const mixTokens: TokenTransferResponse = {
diff --git a/mocks/txs/state.ts b/mocks/txs/state.ts
index d409f4679a..4c19a27d9d 100644
--- a/mocks/txs/state.ts
+++ b/mocks/txs/state.ts
@@ -19,6 +19,7 @@ export const mintToken: TxStateChange = {
direction: 'from',
total: {
token_id: '15077554365819457090226168288698582604878106156134383525616269766016907608065',
+ token_instance: null,
},
},
],
@@ -57,6 +58,7 @@ export const receiveMintedToken: TxStateChange = {
direction: 'to',
total: {
token_id: '15077554365819457090226168288698582604878106156134383525616269766016907608065',
+ token_instance: null,
},
},
],
diff --git a/mocks/txs/tx.ts b/mocks/txs/tx.ts
index c0dd894f38..cb30924455 100644
--- a/mocks/txs/tx.ts
+++ b/mocks/txs/tx.ts
@@ -3,6 +3,7 @@ import type { Transaction } from 'types/api/transaction';
import * as addressMock from 'mocks/address/address';
import { publicTag, privateTag, watchlistName } from 'mocks/address/tag';
+import * as interopMock from 'mocks/interop/interop';
import * as tokenTransferMock from 'mocks/tokens/tokenTransfer';
import * as decodedInputDataMock from 'mocks/txs/decodedInputData';
@@ -423,3 +424,29 @@ export const withRecipientContract = {
...withRecipientEns,
to: addressMock.contract,
};
+
+export const withInteropInMessage: Transaction = {
+ ...base,
+ op_interop: {
+ init_chain: interopMock.chain,
+ nonce: 1,
+ payload: '0x',
+ init_transaction_hash: '0x01a8c328b0370068aaaef49c107f70901cd79adcda81e3599a88855532122e09',
+ sender: addressMock.hash,
+ status: 'Sent',
+ target: addressMock.hash,
+ },
+};
+
+export const withInteropOutMessage: Transaction = {
+ ...base,
+ op_interop: {
+ relay_chain: interopMock.chain,
+ nonce: 1,
+ payload: '0xfa4b78b90000000000000000000000000000000000000000000000000000000005001bcfe835d1028984e9e6e7d016b77164eacbcc6cc061e9333c0b37982b504f7ea791000000000000000000000000a79b29ad7e0196c95b87f4663ded82fbf2e3add8',
+ relay_transaction_hash: '0x01a8c328b0370068aaaef49c107f70901cd79adcda81e3599a88855532122e09',
+ sender: addressMock.hash,
+ status: 'Sent',
+ target: addressMock.hash,
+ },
+};
diff --git a/next-env.d.ts b/next-env.d.ts
index a4a7b3f5cf..52e831b434 100644
--- a/next-env.d.ts
+++ b/next-env.d.ts
@@ -2,4 +2,4 @@
///
// NOTE: This file should not be edited
-// see https://nextjs.org/docs/pages/building-your-application/configuring/typescript for more information.
+// see https://nextjs.org/docs/pages/api-reference/config/typescript for more information.
diff --git a/nextjs/csp/policies/app.ts b/nextjs/csp/policies/app.ts
index d1eda3a8ff..e445281572 100644
--- a/nextjs/csp/policies/app.ts
+++ b/nextjs/csp/policies/app.ts
@@ -71,8 +71,7 @@ export function app(): CspDev.DirectiveDescriptor {
config.app.isDev ? KEY_WORDS.UNSAFE_EVAL : '',
// hash of ColorModeScript: system + dark
- '\'sha256-e7MRMmTzLsLQvIy1iizO1lXf7VWYoQ6ysj5fuUzvRwE=\'',
- '\'sha256-9A7qFFHmxdWjZMQmfzYD2XWaNHLu1ZmQB0Ds4Go764k=\'',
+ '\'sha256-yYJq8IP5/WhJj6zxyTmujEqBFs/MufRufp2QKJFU76M=\'',
// CapybaraRunner
'\'sha256-5+YTmTcBwCYdJ8Jetbr6kyjGp0Ry/H7ptpoun6CrSwQ=\'',
diff --git a/nextjs/getServerSideProps.ts b/nextjs/getServerSideProps.ts
index aab480e91d..e9ea5faab5 100644
--- a/nextjs/getServerSideProps.ts
+++ b/nextjs/getServerSideProps.ts
@@ -46,7 +46,7 @@ Promise>> => {
const isTrackingDisabled = process.env.DISABLE_TRACKING === 'true';
- if (!isTrackingDisabled && !config.app.isDev) {
+ if (!isTrackingDisabled) {
// log pageview
const hostname = req.headers.host;
const timestamp = new Date().toISOString();
@@ -380,6 +380,17 @@ export const mud: GetServerSideProps = async(context) => {
return base(context);
};
+export const interopMessages: GetServerSideProps = async(context) => {
+ const rollupFeature = config.features.rollup;
+ if (!rollupFeature.isEnabled || !rollupFeature.interopEnabled) {
+ return {
+ notFound: true,
+ };
+ }
+
+ return base(context);
+};
+
export const pools: GetServerSideProps = async(context) => {
if (!config.features.pools.isEnabled) {
return {
diff --git a/nextjs/nextjs-routes.d.ts b/nextjs/nextjs-routes.d.ts
index c5d601ba9e..d5c74ed03b 100644
--- a/nextjs/nextjs-routes.d.ts
+++ b/nextjs/nextjs-routes.d.ts
@@ -26,7 +26,6 @@ declare module "nextjs-routes" {
| StaticRoute<"/api/metrics">
| StaticRoute<"/api/monitoring/invalid-api-schema">
| StaticRoute<"/api/proxy">
- | StaticRoute<"/api/sprite">
| StaticRoute<"/api-docs">
| DynamicRoute<"/apps/[id]", { "id": string }>
| StaticRoute<"/apps">
@@ -39,6 +38,7 @@ declare module "nextjs-routes" {
| DynamicRoute<"/block/countdown/[height]", { "height": string }>
| StaticRoute<"/block/countdown">
| StaticRoute<"/blocks">
+ | StaticRoute<"/chakra">
| StaticRoute<"/contract-verification">
| StaticRoute<"/csv-export">
| StaticRoute<"/deposits">
@@ -47,6 +47,7 @@ declare module "nextjs-routes" {
| StaticRoute<"/graphiql">
| StaticRoute<"/">
| StaticRoute<"/internal-txs">
+ | StaticRoute<"/interop-messages">
| StaticRoute<"/login">
| StaticRoute<"/mud-worlds">
| DynamicRoute<"/name-domains/[name]", { "name": string }>
diff --git a/package.json b/package.json
index abf00bd5dc..84b769038b 100644
--- a/package.json
+++ b/package.json
@@ -17,6 +17,8 @@
"start": "next start",
"start:docker:local": "docker run -p 3000:3000 --env-file .env.local blockscout-frontend:local",
"start:docker:preset": "./tools/scripts/docker.preset.sh",
+ "chakra:snippets:add": "chakra snippet add --outdir ./toolkit/chakra",
+ "chakra:typegen": "chakra typegen ./toolkit/theme/theme.ts",
"lint:eslint": "eslint .",
"lint:eslint:fix": "eslint . --fix",
"lint:tsc": "tsc -p ./tsconfig.json",
@@ -36,17 +38,16 @@
"og-image:generate:dev": "./tools/scripts/og-image-generator.dev.sh",
"sitemap:generate:dev": "./tools/scripts/sitemap-generator.dev.sh",
"monitoring:prometheus:local": "docker run --name blockscout_prometheus -d -p 127.0.0.1:9090:9090 -v $(pwd)/prometheus.yml:/etc/prometheus/prometheus.yml prom/prometheus",
- "monitoring:grafana:local": "docker run -d -p 4000:3000 --name=blockscout_grafana --user $(id -u) --volume $(pwd)/grafana:/var/lib/grafana grafana/grafana-enterprise"
+ "monitoring:grafana:local": "docker run -d -p 4000:3000 --name=blockscout_grafana --user $(id -u) --volume $(pwd)/grafana:/var/lib/grafana grafana/grafana-enterprise",
+ "postinstall": "chakra typegen ./toolkit/theme/theme.ts"
},
"dependencies": {
"@blockscout/bens-types": "1.4.1",
"@blockscout/stats-types": "2.5.0-alpha",
"@blockscout/visualizer-types": "0.2.0",
- "@chakra-ui/react": "2.7.1",
- "@chakra-ui/theme-tools": "^2.0.18",
+ "@chakra-ui/react": "3.15.0",
"@cloudnouns/kit": "1.1.6",
- "@emotion/react": "^11.10.4",
- "@emotion/styled": "^11.10.4",
+ "@emotion/react": "11.14.0",
"@growthbook/growthbook-react": "0.21.0",
"@helia/verified-fetch": "2.0.1",
"@hypelab/sdk-react": "^1.0.0",
@@ -54,6 +55,7 @@
"@metamask/providers": "^10.2.1",
"@monaco-editor/react": "^4.4.6",
"@next/bundle-analyzer": "15.0.3",
+ "@opentelemetry/api": "^1.4.1",
"@opentelemetry/auto-instrumentations-node": "0.43.0",
"@opentelemetry/exporter-jaeger": "1.27.0",
"@opentelemetry/exporter-metrics-otlp-proto": "0.49.1",
@@ -62,15 +64,16 @@
"@opentelemetry/sdk-node": "0.49.1",
"@opentelemetry/sdk-trace-node": "1.22.0",
"@opentelemetry/semantic-conventions": "1.22.0",
- "@reown/appkit": "1.6.7",
- "@reown/appkit-adapter-wagmi": "1.6.7",
- "@rollbar/react": "0.12.0-beta",
+ "@reown/appkit": "1.7.0",
+ "@reown/appkit-adapter-wagmi": "1.7.0",
+ "@rollbar/react": "0.12.1",
"@scure/base": "1.1.9",
"@slise/embed-react": "^2.2.0",
"@tanstack/react-query": "5.55.4",
"@tanstack/react-query-devtools": "5.55.4",
"@types/papaparse": "^5.3.5",
"@types/react-scroll": "^1.8.4",
+ "@uidotdev/usehooks": "2.4.1",
"airtable": "^0.12.2",
"bignumber.js": "^9.1.0",
"blo": "^1.1.1",
@@ -82,7 +85,6 @@
"dom-to-image": "^2.6.0",
"es-toolkit": "1.31.0",
"focus-visible": "^5.2.0",
- "framer-motion": "^6.5.1",
"getit-sdk": "^1.0.4",
"gradient-avatar": "git+https://github.com/blockscout/gradient-avatar.git",
"graphiql": "^2.2.0",
@@ -92,7 +94,8 @@
"magic-bytes.js": "1.8.0",
"mixpanel-browser": "^2.47.0",
"monaco-editor": "^0.34.1",
- "next": "15.0.3",
+ "next": "15.2.3",
+ "next-themes": "0.4.4",
"nextjs-routes": "^1.0.8",
"node-fetch": "^3.2.9",
"papaparse": "^5.3.2",
@@ -107,20 +110,22 @@
"react-dom": "18.3.1",
"react-google-recaptcha": "3.1.0",
"react-hook-form": "7.52.1",
+ "react-icons": "5.4.0",
"react-identicons": "^1.2.5",
"react-intersection-observer": "^9.5.2",
"react-jazzicon": "^1.0.4",
"react-number-format": "^5.3.1",
"react-scroll": "^1.8.7",
"rollbar": "2.26.4",
- "swagger-ui-react": "^5.9.0",
+ "swagger-ui-react": "5.20.3",
"use-font-face-observer": "^1.2.1",
"valibot": "0.38.0",
- "viem": "2.21.54",
- "wagmi": "2.14.0",
+ "viem": "2.23.14",
+ "wagmi": "2.14.15",
"xss": "^1.0.14"
},
"devDependencies": {
+ "@chakra-ui/cli": "3.15.0",
"@eslint/compat": "1.2.2",
"@eslint/js": "9.14.0",
"@next/eslint-plugin-next": "15.0.3",
@@ -135,7 +140,7 @@
"@types/csp-dev": "^1.0.0",
"@types/d3": "^7.4.0",
"@types/dom-to-image": "^2.6.4",
- "@types/jest": "^29.2.0",
+ "@types/jest": "29.2.1",
"@types/js-cookie": "^3.0.2",
"@types/mixpanel-browser": "^2.38.1",
"@types/node": "20.16.7",
@@ -144,7 +149,7 @@
"@types/react": "18.3.12",
"@types/react-dom": "18.3.1",
"@types/react-google-recaptcha": "^2.1.5",
- "@types/swagger-ui-react": "^4.11.0",
+ "@types/swagger-ui-react": "5.18.0",
"@types/ws": "^8.5.3",
"@typescript-eslint/eslint-plugin": "^5.60.0",
"@vitejs/plugin-react": "^4.0.0",
diff --git a/pages/404.tsx b/pages/404.tsx
index bd4e020c0d..f19180f980 100644
--- a/pages/404.tsx
+++ b/pages/404.tsx
@@ -4,19 +4,12 @@ import type { NextPageWithLayout } from 'nextjs/types';
import PageNextJs from 'nextjs/PageNextJs';
-import { useRollbar } from 'lib/rollbar';
import AppError from 'ui/shared/AppError/AppError';
import LayoutError from 'ui/shared/layout/LayoutError';
const error = new Error('Not found', { cause: { status: 404 } });
const Page: NextPageWithLayout = () => {
- const rollbar = useRollbar();
-
- React.useEffect(() => {
- rollbar?.error('Page not found');
- }, [ rollbar ]);
-
return (
diff --git a/pages/_app.tsx b/pages/_app.tsx
index 01a8841550..8b52030b34 100644
--- a/pages/_app.tsx
+++ b/pages/_app.tsx
@@ -1,4 +1,4 @@
-import { type ChakraProps } from '@chakra-ui/react';
+import type { HTMLChakraProps } from '@chakra-ui/react';
import { GrowthBookProvider } from '@growthbook/growthbook-react';
import { QueryClientProvider } from '@tanstack/react-query';
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
@@ -10,7 +10,6 @@ import type { NextPageWithLayout } from 'nextjs/types';
import config from 'configs/app';
import useQueryClientConfig from 'lib/api/useQueryClientConfig';
import { AppContextProvider } from 'lib/contexts/app';
-import { ChakraProvider } from 'lib/contexts/chakra';
import { MarketplaceContextProvider } from 'lib/contexts/marketplace';
import { RewardsContextProvider } from 'lib/contexts/rewards';
import { ScrollDirectionProvider } from 'lib/contexts/scrollDirection';
@@ -20,6 +19,8 @@ import useLoadFeatures from 'lib/growthbook/useLoadFeatures';
import useNotifyOnNavigation from 'lib/hooks/useNotifyOnNavigation';
import { clientConfig as rollbarConfig, Provider as RollbarProvider } from 'lib/rollbar';
import { SocketProvider } from 'lib/socket/context';
+import { Provider as ChakraProvider } from 'toolkit/chakra/provider';
+import { Toaster } from 'toolkit/chakra/toaster';
import RewardsLoginModal from 'ui/rewards/login/RewardsLoginModal';
import AppErrorBoundary from 'ui/shared/AppError/AppErrorBoundary';
import AppErrorGlobalContainer from 'ui/shared/AppError/AppErrorGlobalContainer';
@@ -34,7 +35,7 @@ type AppPropsWithLayout = AppProps & {
Component: NextPageWithLayout;
};
-const ERROR_SCREEN_STYLES: ChakraProps = {
+const ERROR_SCREEN_STYLES: HTMLChakraProps<'div'> = {
h: '100vh',
display: 'flex',
flexDirection: 'column',
@@ -47,16 +48,29 @@ const ERROR_SCREEN_STYLES: ChakraProps = {
};
function MyApp({ Component, pageProps }: AppPropsWithLayout) {
+ // to avoid hydration mismatch between server and client
+ // we have to render the app only on client (when it is mounted)
+ // https://github.com/pacocoursey/next-themes?tab=readme-ov-file#avoid-hydration-mismatch
+ const [ mounted, setMounted ] = React.useState(false);
+
+ React.useEffect(() => {
+ setMounted(true);
+ }, []);
+
useLoadFeatures(pageProps.uuid);
useNotifyOnNavigation();
const growthBook = initGrowthBook(pageProps.uuid);
const queryClient = useQueryClientConfig();
+ if (!mounted) {
+ return null;
+ }
+
const getLayout = Component.getLayout ?? ((page) => { page } );
return (
-
+
{ getLayout( ) }
+
{ config.features.rewards.isEnabled && }
diff --git a/pages/_document.tsx b/pages/_document.tsx
index 1706e247ac..9032774cbe 100644
--- a/pages/_document.tsx
+++ b/pages/_document.tsx
@@ -1,4 +1,3 @@
-import { ColorModeScript } from '@chakra-ui/react';
import type { DocumentContext } from 'next/document';
import Document, { Html, Head, Main, NextScript } from 'next/document';
import React from 'react';
@@ -7,7 +6,6 @@ import logRequestFromBot from 'nextjs/utils/logRequestFromBot';
import * as serverTiming from 'nextjs/utils/serverTiming';
import config from 'configs/app';
-import theme from 'theme/theme';
import * as svgSprite from 'ui/shared/IconSvg';
class MyDocument extends Document {
@@ -57,7 +55,6 @@ class MyDocument extends Document {
-
diff --git a/pages/api/sprite.ts b/pages/api/sprite.ts
deleted file mode 100644
index 9ba9d7030c..0000000000
--- a/pages/api/sprite.ts
+++ /dev/null
@@ -1,49 +0,0 @@
-import fs from 'fs';
-import type { NextApiRequest, NextApiResponse } from 'next';
-import path from 'path';
-
-import config from 'configs/app';
-
-const ROOT_DIR = './icons';
-const NAME_PREFIX = ROOT_DIR.replace('./', '') + '/';
-
-interface IconInfo {
- name: string;
- fileSize: number;
-}
-
-const getIconName = (filePath: string) => filePath.replace(NAME_PREFIX, '').replace('.svg', '');
-
-function collectIconNames(dir: string) {
- const files = fs.readdirSync(dir, { withFileTypes: true });
- let icons: Array = [];
-
- files.forEach((file) => {
- const filePath = path.join(dir, file.name);
- const stats = fs.statSync(filePath);
-
- file.name.endsWith('.svg') && icons.push({
- name: getIconName(filePath),
- fileSize: stats.size,
- });
-
- if (file.isDirectory()) {
- icons = [ ...icons, ...collectIconNames(filePath) ];
- }
- });
-
- return icons;
-}
-
-export default async function spriteHandler(req: NextApiRequest, res: NextApiResponse) {
-
- if (!config.app.isDev) {
- return res.status(404).json({ error: 'Not found' });
- }
-
- const icons = collectIconNames(ROOT_DIR);
-
- res.status(200).json({
- icons,
- });
-}
diff --git a/pages/chakra.tsx b/pages/chakra.tsx
new file mode 100644
index 0000000000..87c61a1f74
--- /dev/null
+++ b/pages/chakra.tsx
@@ -0,0 +1,18 @@
+import type { NextPage } from 'next';
+import React from 'react';
+
+import PageNextJs from 'nextjs/PageNextJs';
+
+import Chakra from 'ui/pages/Chakra';
+
+const Page: NextPage = () => {
+ return (
+
+
+
+ );
+};
+
+export default Page;
+
+export { base as getServerSideProps } from 'nextjs/getServerSideProps';
diff --git a/pages/interop-messages.tsx b/pages/interop-messages.tsx
new file mode 100644
index 0000000000..4570647127
--- /dev/null
+++ b/pages/interop-messages.tsx
@@ -0,0 +1,19 @@
+import type { NextPage } from 'next';
+import dynamic from 'next/dynamic';
+import React from 'react';
+
+import PageNextJs from 'nextjs/PageNextJs';
+
+const InteropMessages = dynamic(() => import('ui/pages/InteropMessages'), { ssr: false });
+
+const Page: NextPage = () => {
+ return (
+
+
+
+ );
+};
+
+export default Page;
+
+export { interopMessages as getServerSideProps } from 'nextjs/getServerSideProps';
diff --git a/pages/stats.tsx b/pages/stats/index.tsx
similarity index 100%
rename from pages/stats.tsx
rename to pages/stats/index.tsx
diff --git a/playwright/TestApp.tsx b/playwright/TestApp.tsx
index 5c09d7b166..7eaedeb6b2 100644
--- a/playwright/TestApp.tsx
+++ b/playwright/TestApp.tsx
@@ -1,4 +1,3 @@
-import { ChakraProvider } from '@chakra-ui/react';
import { GrowthBookProvider } from '@growthbook/growthbook-react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import React from 'react';
@@ -15,7 +14,7 @@ import { RewardsContextProvider } from 'lib/contexts/rewards';
import { SettingsContextProvider } from 'lib/contexts/settings';
import { SocketProvider } from 'lib/socket/context';
import { currentChain } from 'lib/web3/chains';
-import theme from 'theme/theme';
+import { Provider as ChakraProvider } from 'toolkit/chakra/provider';
import { port as socketPort } from './utils/socket';
@@ -73,7 +72,7 @@ const TestApp = ({ children, withSocket, appContext = defaultAppContext, marketp
}));
return (
-
+
diff --git a/playwright/fixtures/mockEnvs.ts b/playwright/fixtures/mockEnvs.ts
index a723b9c62d..6033c83f77 100644
--- a/playwright/fixtures/mockEnvs.ts
+++ b/playwright/fixtures/mockEnvs.ts
@@ -97,4 +97,10 @@ export const ENVS_MAP: Record> = {
externalTxs: [
[ 'NEXT_PUBLIC_TX_EXTERNAL_TRANSACTIONS_CONFIG', '{"chain_name": "Solana", "chain_logo_url": "http://example.url", "explorer_url_template": "https://scan.io/tx/{hash}"}' ],
],
+ interop: [
+ [ 'NEXT_PUBLIC_ROLLUP_TYPE', 'optimistic' ],
+ [ 'NEXT_PUBLIC_ROLLUP_L1_BASE_URL', 'https://localhost:3101' ],
+ [ 'NEXT_PUBLIC_ROLLUP_L2_WITHDRAWAL_URL', 'https://localhost:3102' ],
+ [ 'NEXT_PUBLIC_INTEROP_ENABLED', 'true' ],
+ ],
};
diff --git a/public/icons/name.d.ts b/public/icons/name.d.ts
index f10b3053c0..0d29fd5579 100644
--- a/public/icons/name.d.ts
+++ b/public/icons/name.d.ts
@@ -35,6 +35,7 @@
| "checkered_flag"
| "clock-light"
| "clock"
+ | "close"
| "coins/bitcoin"
| "collection"
| "columns"
@@ -80,10 +81,12 @@
| "heart_filled"
| "heart_outline"
| "hourglass"
+ | "info_filled"
| "info"
| "integration/full"
| "integration/partial"
| "internal_txns"
+ | "interop"
| "key"
| "lightning_navbar"
| "lightning"
diff --git a/public/static/badges.svg b/public/static/merits/badges.svg
similarity index 100%
rename from public/static/badges.svg
rename to public/static/merits/badges.svg
diff --git a/public/static/merits/campaigns.svg b/public/static/merits/campaigns.svg
new file mode 100644
index 0000000000..efbd2064c4
--- /dev/null
+++ b/public/static/merits/campaigns.svg
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/public/static/merits_program.png b/public/static/merits/merits_program.png
similarity index 100%
rename from public/static/merits_program.png
rename to public/static/merits/merits_program.png
diff --git a/public/static/merits/offers.svg b/public/static/merits/offers.svg
new file mode 100644
index 0000000000..7f7f218458
--- /dev/null
+++ b/public/static/merits/offers.svg
@@ -0,0 +1,186 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/stubs/interop.ts b/stubs/interop.ts
new file mode 100644
index 0000000000..a5dfb525aa
--- /dev/null
+++ b/stubs/interop.ts
@@ -0,0 +1,21 @@
+import type { InteropMessage } from 'types/api/interop';
+
+import { ADDRESS_HASH } from './addressParams';
+import { TX_HASH } from './tx';
+
+export const INTEROP_MESSAGE: InteropMessage = {
+ init_transaction_hash: TX_HASH,
+ nonce: 52,
+ payload: '0x4f0edcc90000000000000000000000007da521cbbe62e89cd75e0993c78b8c68c25f696b',
+ relay_chain: {
+ chain_id: 420120000,
+ chain_name: 'Optimism Testnet',
+ chain_logo: null,
+ instance_url: 'https://optimism-interop-alpha-0.blockscout.com/',
+ },
+ relay_transaction_hash: TX_HASH,
+ sender: ADDRESS_HASH,
+ status: 'Relayed',
+ target: ADDRESS_HASH,
+ timestamp: '2025-02-20T01:05:14.000000Z',
+};
diff --git a/stubs/token.ts b/stubs/token.ts
index c301ff7cd2..d371628505 100644
--- a/stubs/token.ts
+++ b/stubs/token.ts
@@ -110,6 +110,7 @@ export const TOKEN_TRANSFER_ERC_721: TokenTransfer = {
...TOKEN_TRANSFER_ERC_20,
total: {
token_id: '35870',
+ token_instance: null,
},
token: TOKEN_INFO_ERC_721,
};
@@ -120,6 +121,7 @@ export const TOKEN_TRANSFER_ERC_1155: TokenTransfer = {
token_id: '35870',
value: '123',
decimals: '18',
+ token_instance: null,
},
token: TOKEN_INFO_ERC_1155,
};
@@ -130,6 +132,7 @@ export const TOKEN_TRANSFER_ERC_404: TokenTransfer = {
token_id: '35870',
value: '123',
decimals: '18',
+ token_instance: null,
},
token: TOKEN_INFO_ERC_404,
};
diff --git a/stubs/txStateChanges.ts b/stubs/txStateChanges.ts
index 7a26ee8a77..f843c8c3ae 100644
--- a/stubs/txStateChanges.ts
+++ b/stubs/txStateChanges.ts
@@ -32,6 +32,7 @@ export const STATE_CHANGE_TOKEN: TxStateChange = {
direction: 'to',
total: {
token_id: '1621395',
+ token_instance: null,
},
},
],
diff --git a/theme/.gitignore b/theme/.gitignore
deleted file mode 100644
index b2d59d1f75..0000000000
--- a/theme/.gitignore
+++ /dev/null
@@ -1,2 +0,0 @@
-/node_modules
-/dist
\ No newline at end of file
diff --git a/theme/components/Alert/Alert.pw.tsx b/theme/components/Alert/Alert.pw.tsx
deleted file mode 100644
index 144a4540e8..0000000000
--- a/theme/components/Alert/Alert.pw.tsx
+++ /dev/null
@@ -1,43 +0,0 @@
-import type { AlertProps } from '@chakra-ui/react';
-import { Alert, AlertIcon, AlertTitle } from '@chakra-ui/react';
-import React from 'react';
-
-import { test, expect } from 'playwright/lib';
-
-test.use({ viewport: { width: 400, height: 720 } });
-
-const TEST_CASES: Array = [
- {
- status: 'info',
- },
- {
- status: 'warning',
- },
- {
- status: 'success',
- },
- {
- status: 'error',
- },
- {
- status: 'info',
- colorScheme: 'gray',
- },
-];
-
-TEST_CASES.forEach((props) => {
- const testName = Object.entries(props).map(([ key, value ]) => `${ key }=${ value }`).join(', ');
-
- test(`${ testName } +@dark-mode`, async({ render }) => {
- const component = await render(
-
-
-
- This is alert text
-
- ,
- );
-
- await expect(component).toHaveScreenshot();
- });
-});
diff --git a/theme/components/Alert/Alert.ts b/theme/components/Alert/Alert.ts
deleted file mode 100644
index acce04ee0e..0000000000
--- a/theme/components/Alert/Alert.ts
+++ /dev/null
@@ -1,97 +0,0 @@
-import { alertAnatomy as parts } from '@chakra-ui/anatomy';
-import type { StyleFunctionProps } from '@chakra-ui/styled-system';
-import { createMultiStyleConfigHelpers, cssVar } from '@chakra-ui/styled-system';
-import { transparentize } from '@chakra-ui/theme-tools';
-
-const { definePartsStyle, defineMultiStyleConfig } =
- createMultiStyleConfigHelpers(parts.keys);
-
-const $fg = cssVar('alert-fg');
-const $bg = cssVar('alert-bg');
-
-function getBg(props: StyleFunctionProps) {
- const { theme, colorScheme: c } = props;
- const darkBg = transparentize(`${ c }.200`, 0.16)(theme);
- return {
- light: `colors.${ c }.100`,
- dark: darkBg,
- };
-}
-
-const baseStyle = definePartsStyle({
- container: {
- bg: $bg.reference,
- borderRadius: 'md',
- px: 6,
- py: 3,
- },
- title: {
- fontWeight: 'bold',
- lineHeight: '6',
- marginEnd: '2',
- },
- description: {
- lineHeight: '6',
- },
- icon: {
- color: $fg.reference,
- flexShrink: 0,
- marginEnd: '3',
- w: '5',
- h: '6',
- },
- spinner: {
- color: $fg.reference,
- flexShrink: 0,
- marginEnd: '3',
- w: '5',
- h: '5',
- },
-});
-
-const variantSubtle = definePartsStyle((props) => {
- const { colorScheme } = props;
- const bg = getBg(props);
-
- return {
- container: {
- [$fg.variable]: colorScheme === 'gray' ? 'colors.blackAlpha.800' : `colors.${ colorScheme }.500`,
- [$bg.variable]: colorScheme === 'gray' ? 'colors.blackAlpha.100' : bg.light,
- _dark: {
- [$fg.variable]: colorScheme === 'gray' ? 'colors.whiteAlpha.800' : `colors.${ colorScheme }.200`,
- [$bg.variable]: colorScheme === 'gray' ? 'colors.whiteAlpha.200' : bg.dark,
- },
- },
- };
-});
-
-const variantSolid = definePartsStyle((props) => {
- const { colorScheme: c } = props;
- return {
- container: {
- [$fg.variable]: `colors.white`,
- [$bg.variable]: `colors.${ c }.500`,
- _dark: {
- [$fg.variable]: `colors.gray.900`,
- [$bg.variable]: `colors.${ c }.200`,
- },
- color: $fg.reference,
- },
- };
-});
-
-const variants = {
- subtle: variantSubtle,
- solid: variantSolid,
-};
-
-const Alert = defineMultiStyleConfig({
- baseStyle,
- variants,
- defaultProps: {
- variant: 'subtle',
- colorScheme: 'blue',
- },
-});
-
-export default Alert;
diff --git a/theme/components/Alert/__screenshots__/Alert.pw.tsx_dark-color-mode_status-error-dark-mode-1.png b/theme/components/Alert/__screenshots__/Alert.pw.tsx_dark-color-mode_status-error-dark-mode-1.png
deleted file mode 100644
index d1f0f8e805..0000000000
Binary files a/theme/components/Alert/__screenshots__/Alert.pw.tsx_dark-color-mode_status-error-dark-mode-1.png and /dev/null differ
diff --git a/theme/components/Alert/__screenshots__/Alert.pw.tsx_dark-color-mode_status-info-colorScheme-gray-dark-mode-1.png b/theme/components/Alert/__screenshots__/Alert.pw.tsx_dark-color-mode_status-info-colorScheme-gray-dark-mode-1.png
deleted file mode 100644
index 259f974e96..0000000000
Binary files a/theme/components/Alert/__screenshots__/Alert.pw.tsx_dark-color-mode_status-info-colorScheme-gray-dark-mode-1.png and /dev/null differ
diff --git a/theme/components/Alert/__screenshots__/Alert.pw.tsx_dark-color-mode_status-info-dark-mode-1.png b/theme/components/Alert/__screenshots__/Alert.pw.tsx_dark-color-mode_status-info-dark-mode-1.png
deleted file mode 100644
index 34f5fbbf66..0000000000
Binary files a/theme/components/Alert/__screenshots__/Alert.pw.tsx_dark-color-mode_status-info-dark-mode-1.png and /dev/null differ
diff --git a/theme/components/Alert/__screenshots__/Alert.pw.tsx_dark-color-mode_status-success-dark-mode-1.png b/theme/components/Alert/__screenshots__/Alert.pw.tsx_dark-color-mode_status-success-dark-mode-1.png
deleted file mode 100644
index 12ca38da9c..0000000000
Binary files a/theme/components/Alert/__screenshots__/Alert.pw.tsx_dark-color-mode_status-success-dark-mode-1.png and /dev/null differ
diff --git a/theme/components/Alert/__screenshots__/Alert.pw.tsx_dark-color-mode_status-warning-dark-mode-1.png b/theme/components/Alert/__screenshots__/Alert.pw.tsx_dark-color-mode_status-warning-dark-mode-1.png
deleted file mode 100644
index 7b73b0cac1..0000000000
Binary files a/theme/components/Alert/__screenshots__/Alert.pw.tsx_dark-color-mode_status-warning-dark-mode-1.png and /dev/null differ
diff --git a/theme/components/Alert/__screenshots__/Alert.pw.tsx_default_status-error-dark-mode-1.png b/theme/components/Alert/__screenshots__/Alert.pw.tsx_default_status-error-dark-mode-1.png
deleted file mode 100644
index 03427b8929..0000000000
Binary files a/theme/components/Alert/__screenshots__/Alert.pw.tsx_default_status-error-dark-mode-1.png and /dev/null differ
diff --git a/theme/components/Alert/__screenshots__/Alert.pw.tsx_default_status-info-colorScheme-gray-dark-mode-1.png b/theme/components/Alert/__screenshots__/Alert.pw.tsx_default_status-info-colorScheme-gray-dark-mode-1.png
deleted file mode 100644
index 2f5c4ef2a6..0000000000
Binary files a/theme/components/Alert/__screenshots__/Alert.pw.tsx_default_status-info-colorScheme-gray-dark-mode-1.png and /dev/null differ
diff --git a/theme/components/Alert/__screenshots__/Alert.pw.tsx_default_status-info-dark-mode-1.png b/theme/components/Alert/__screenshots__/Alert.pw.tsx_default_status-info-dark-mode-1.png
deleted file mode 100644
index 7bb647aba2..0000000000
Binary files a/theme/components/Alert/__screenshots__/Alert.pw.tsx_default_status-info-dark-mode-1.png and /dev/null differ
diff --git a/theme/components/Alert/__screenshots__/Alert.pw.tsx_default_status-success-dark-mode-1.png b/theme/components/Alert/__screenshots__/Alert.pw.tsx_default_status-success-dark-mode-1.png
deleted file mode 100644
index 5dd37bf3b3..0000000000
Binary files a/theme/components/Alert/__screenshots__/Alert.pw.tsx_default_status-success-dark-mode-1.png and /dev/null differ
diff --git a/theme/components/Alert/__screenshots__/Alert.pw.tsx_default_status-warning-dark-mode-1.png b/theme/components/Alert/__screenshots__/Alert.pw.tsx_default_status-warning-dark-mode-1.png
deleted file mode 100644
index 75d62d855b..0000000000
Binary files a/theme/components/Alert/__screenshots__/Alert.pw.tsx_default_status-warning-dark-mode-1.png and /dev/null differ
diff --git a/theme/components/Badge.ts b/theme/components/Badge.ts
deleted file mode 100644
index ab0b13a491..0000000000
--- a/theme/components/Badge.ts
+++ /dev/null
@@ -1,63 +0,0 @@
-import { defineStyle, defineStyleConfig } from '@chakra-ui/styled-system';
-import { mode } from '@chakra-ui/theme-tools';
-
-const baseStyle = defineStyle({
- fontSize: 'xs',
- borderRadius: 'sm',
- fontWeight: 'bold',
-});
-
-const variantSubtle = defineStyle((props) => {
- const { colorScheme: c } = props;
-
- if (c === 'gray') {
- return {
- bg: mode('blackAlpha.50', 'whiteAlpha.100')(props),
- color: mode('blackAlpha.800', 'whiteAlpha.800')(props),
- };
- }
-
- if (c === 'gray-blue') {
- return {
- bg: mode('gray.100', 'gray.800')(props),
- color: mode('blackAlpha.800', 'whiteAlpha.800')(props),
- _hover: {
- opacity: 0.76,
- },
- };
- }
-
- if (c === 'black-blue') {
- return {
- bg: mode('blue.50', 'blue.800')(props),
- color: mode('blackAlpha.800', 'whiteAlpha.800')(props),
- };
- }
-
- if (c === 'black-purple') {
- return {
- bg: mode('purple.100', 'purple.800')(props),
- color: mode('blackAlpha.800', 'whiteAlpha.800')(props),
- };
- }
-
- return {
- bg: mode(`${ c }.50`, `${ c }.800`)(props),
- color: mode(`${ c }.500`, `${ c }.100`)(props),
- };
-});
-
-const variants = {
- subtle: variantSubtle,
-};
-
-const Badge = defineStyleConfig({
- baseStyle,
- variants,
- defaultProps: {
- variant: 'subtle',
- colorScheme: 'gray',
- },
-});
-
-export default Badge;
diff --git a/theme/components/Button/Button.pw.tsx b/theme/components/Button/Button.pw.tsx
deleted file mode 100644
index 4435715b88..0000000000
--- a/theme/components/Button/Button.pw.tsx
+++ /dev/null
@@ -1,78 +0,0 @@
-import { Box, Button, Flex } from '@chakra-ui/react';
-import React from 'react';
-
-import { test, expect } from 'playwright/lib';
-
-test.use({ viewport: { width: 150, height: 350 } });
-
-[
- { variant: 'solid', states: [ 'default', 'disabled', 'hovered', 'active' ] },
- { variant: 'outline', colorScheme: 'gray', withDarkMode: true, states: [ 'default', 'disabled', 'hovered', 'active', 'selected' ] },
- { variant: 'outline', colorScheme: 'blue', withDarkMode: true, states: [ 'default', 'disabled', 'hovered', 'active', 'selected' ] },
- { variant: 'simple', withDarkMode: true, states: [ 'default', 'hovered' ] },
- { variant: 'ghost', withDarkMode: true, states: [ 'default', 'hovered', 'active' ] },
- { variant: 'subtle', states: [ 'default', 'hovered' ] },
- { variant: 'subtle', colorScheme: 'gray', states: [ 'default', 'hovered' ], withDarkMode: true },
- { variant: 'hero', states: [ 'default', 'hovered' ], withDarkMode: true },
- { variant: 'header', states: [ 'default', 'hovered', 'selected' ], withDarkMode: true },
- { variant: 'radio_group', states: [ 'default', 'hovered', 'selected' ], withDarkMode: true },
-].forEach(({ variant, colorScheme, withDarkMode, states }) => {
- test.describe(`variant ${ variant }${ colorScheme ? ` with ${ colorScheme } color scheme` : '' }${ withDarkMode ? ' +@dark-mode' : '' }`, () => {
- test('base view', async({ render }) => {
- const component = await render(
-
- { states?.map((state) => {
- switch (state) {
- case 'default': {
- return (
-
- Default:
- Click me
-
- );
- }
- case 'disabled': {
- return (
-
- Disabled:
- Click me
-
- );
- }
- case 'active': {
- return (
-
- Active:
- Click me
-
- );
- }
- case 'hovered': {
- return (
-
- Hovered:
- Hover me
-
- );
- }
- case 'selected': {
- return (
-
- Selected:
- Click me
-
- );
- }
-
- default: {
- return null;
- }
- }
- }) }
- ,
- );
- await component.getByText('Hover me').hover();
- await expect(component).toHaveScreenshot();
- });
- });
-});
diff --git a/theme/components/Button/Button.ts b/theme/components/Button/Button.ts
deleted file mode 100644
index 7f6ea7605f..0000000000
--- a/theme/components/Button/Button.ts
+++ /dev/null
@@ -1,315 +0,0 @@
-import { defineStyle, defineStyleConfig } from '@chakra-ui/styled-system';
-import { mode } from '@chakra-ui/theme-tools';
-import { runIfFn } from '@chakra-ui/utils';
-
-import config from 'configs/app';
-
-const variantSolid = defineStyle((props) => {
- const { colorScheme: c } = props;
-
- const bg = `${ c }.600`;
- const color = 'white';
- const hoverBg = `${ c }.400`;
- const activeBg = hoverBg;
-
- return {
- bg,
- color,
- _hover: {
- bg: hoverBg,
- _disabled: {
- bg,
- },
- },
- _disabled: {
- opacity: 0.2,
- },
- // According to design there is no "active" or "pressed" state
- // It is simply should be the same as the "hover" state
- _active: { bg: activeBg },
- fontWeight: 600,
- };
-});
-
-const variantOutline = defineStyle((props) => {
- const { colorScheme: c } = props;
-
- const isGrayTheme = c === 'gray';
-
- const bg = 'transparent';
-
- const color = isGrayTheme ? mode('blackAlpha.800', 'whiteAlpha.800')(props) : mode(`${ c }.600`, `${ c }.300`)(props);
- const borderColor = isGrayTheme ? mode('gray.200', 'gray.600')(props) : mode(`${ c }.600`, `${ c }.300`)(props);
-
- const selectedBg = isGrayTheme ? mode('blue.50', 'gray.600')(props) : mode(`${ c }.50`, 'gray.600')(props);
- const selectedColor = mode('blue.600', 'gray.50')(props);
-
- return {
- color,
- fontWeight: props.fontWeight || 600,
- borderWidth: props.borderWidth || '2px',
- borderStyle: 'solid',
- borderColor,
- bg,
- _hover: {
- color: 'link_hovered',
- borderColor: 'link_hovered',
- bg,
- span: {
- color: 'link_hovered',
- },
- _disabled: {
- color,
- borderColor,
- },
- },
- _disabled: {
- opacity: 0.2,
- },
- // According to design there is no "active" or "pressed" state
- // It is simply should be the same as the "hover" state
- _active: {
- color: 'link_hovered',
- borderColor: 'link_hovered',
- bg,
- span: {
- color: 'link_hovered',
- },
- _disabled: {
- color: 'link_hovered',
- borderColor: 'link_hovered',
- },
- },
- // We have a special state for this button variant that serves as a popover trigger.
- // When any items (filters) are selected in the popover, the button should change its background and text color.
- // The last CSS selector is for redefining styles for the TabList component.
- [`
- &[data-selected=true],
- &[data-selected=true][aria-selected=true]
- `]: {
- bg: selectedBg,
- color: selectedColor,
- borderColor: selectedBg,
- },
- };
-});
-
-const variantRadioGroup = defineStyle((props) => {
- const outline = runIfFn(variantOutline, props);
- const bgColor = mode('blue.50', 'gray.800')(props);
- const selectedTextColor = mode('blue.700', 'gray.50')(props);
-
- return {
- ...outline,
- fontWeight: 500,
- cursor: 'pointer',
- bgColor: 'none',
- borderColor: bgColor,
- _hover: {
- borderColor: bgColor,
- color: 'link_hovered',
- },
- _active: {
- bgColor: 'none',
- },
- _notFirst: {
- borderLeftWidth: 0,
- },
- // We have a special state for this button variant that serves as a popover trigger.
- // When any items (filters) are selected in the popover, the button should change its background and text color.
- // The last CSS selector is for redefining styles for the TabList component.
- [`
- &[data-selected=true],
- &[data-selected=true][aria-selected=true]
- `]: {
- cursor: 'initial',
- bgColor,
- borderColor: bgColor,
- color: selectedTextColor,
- _hover: {
- color: selectedTextColor,
- },
- },
- };
-});
-
-const variantSimple = defineStyle((props) => {
- const outline = runIfFn(variantOutline, props);
-
- return {
- color: outline.color,
- _hover: {
- color: outline._hover.color,
- },
- };
-});
-
-const variantGhost = defineStyle((props) => {
- const { colorScheme: c } = props;
- const activeBg = mode(`${ c }.50`, 'gray.800')(props);
-
- return {
- bg: 'transparent',
- color: mode(`${ c }.700`, 'gray.400')(props),
- _active: {
- color: mode(`${ c }.700`, 'gray.50')(props),
- bg: mode(`${ c }.50`, 'gray.800')(props),
- },
- _hover: {
- color: `${ c }.400`,
- _active: {
- bg: props.isActive ? activeBg : 'transparent',
- color: mode(`${ c }.700`, 'gray.50')(props),
- },
- },
- };
-});
-
-const variantSubtle = defineStyle((props) => {
- const { colorScheme: c } = props;
-
- if (c === 'gray') {
- return {
- bg: mode('blackAlpha.200', 'whiteAlpha.200')(props),
- color: mode('blackAlpha.800', 'whiteAlpha.800')(props),
- _hover: {
- color: 'link_hovered',
- _disabled: {
- color: mode('blackAlpha.800', 'whiteAlpha.800')(props),
- bg: mode('blackAlpha.200', 'whiteAlpha.200')(props),
- },
- },
- };
- }
-
- return {
- bg: `${ c }.100`,
- color: `${ c }.600`,
- _hover: {
- color: 'link_hovered',
- },
- };
-});
-
-// for buttons in the hero banner
-const variantHero = defineStyle((props) => {
- const buttonConfig = config.UI.homepage.heroBanner?.button;
- return {
- bg: mode(
- buttonConfig?._default?.background?.[0] || 'blue.600',
- buttonConfig?._default?.background?.[1] || buttonConfig?._default?.background?.[0] || 'blue.600',
- )(props),
- color: mode(
- buttonConfig?._default?.text_color?.[0] || 'white',
- buttonConfig?._default?.text_color?.[1] || buttonConfig?._default?.text_color?.[0] || 'white',
- )(props),
- _hover: {
- bg: mode(
- buttonConfig?._hover?.background?.[0] || 'blue.400',
- buttonConfig?._hover?.background?.[1] || buttonConfig?._hover?.background?.[0] || 'blue.400',
- )(props),
- color: mode(
- buttonConfig?._hover?.text_color?.[0] || 'white',
- buttonConfig?._hover?.text_color?.[1] || buttonConfig?._hover?.text_color?.[0] || 'white',
- )(props),
- },
- '&[data-selected=true]': {
- bg: mode(
- buttonConfig?._selected?.background?.[0] || 'blue.50',
- buttonConfig?._selected?.background?.[1] || buttonConfig?._selected?.background?.[0] || 'blue.50',
- )(props),
- color: mode(
- buttonConfig?._selected?.text_color?.[0] || 'blackAlpha.800',
- buttonConfig?._selected?.text_color?.[1] || buttonConfig?._selected?.text_color?.[0] || 'blackAlpha.800',
- )(props),
- },
- };
-});
-
-// for buttons in the page header
-const variantHeader = defineStyle((props) => {
-
- return {
- bgColor: 'transparent',
- color: mode('blackAlpha.800', 'gray.400')(props),
- borderColor: mode('gray.300', 'gray.600')(props),
- borderWidth: props.borderWidth || '2px',
- borderStyle: 'solid',
- _hover: {
- color: 'link_hovered',
- borderColor: 'link_hovered',
- },
- '&[data-selected=true]': {
- bgColor: mode('blackAlpha.50', 'whiteAlpha.100')(props),
- color: mode('blackAlpha.800', 'whiteAlpha.800')(props),
- borderColor: 'transparent',
- borderWidth: props.borderWidth || '0px',
- },
- '&[data-selected=true][data-warning=true]': {
- bgColor: mode('orange.100', 'orange.900')(props),
- color: mode('blackAlpha.800', 'whiteAlpha.800')(props),
- borderColor: 'transparent',
- borderWidth: props.borderWidth || '0px',
- },
- };
-});
-
-const variants = {
- solid: variantSolid,
- outline: variantOutline,
- simple: variantSimple,
- ghost: variantGhost,
- subtle: variantSubtle,
- hero: variantHero,
- header: variantHeader,
- radio_group: variantRadioGroup,
-};
-
-const baseStyle = defineStyle({
- fontWeight: 600,
- borderRadius: 'base',
- overflow: 'hidden',
- _focusVisible: {
- boxShadow: { base: 'none', lg: 'outline' },
- },
-});
-
-const sizes = {
- lg: defineStyle({
- h: 12,
- minW: 'unset',
- fontSize: 'lg',
- px: 6,
- }),
- md: defineStyle({
- h: 10,
- minW: 'unset',
- fontSize: 'md',
- px: 4,
- }),
- sm: defineStyle({
- h: 8,
- minW: 'unset',
- fontSize: 'sm',
- px: 3,
- }),
- xs: defineStyle({
- h: 6,
- minW: 'unset',
- fontSize: 'xs',
- px: 2,
- }),
-};
-
-const Button = defineStyleConfig({
- baseStyle,
- variants,
- sizes,
- defaultProps: {
- variant: 'solid',
- size: 'md',
- colorScheme: 'blue',
- },
-});
-
-export default Button;
diff --git a/theme/components/Button/__screenshots__/Button.pw.tsx_dark-color-mode_variant-ghost-dark-mode-base-view-1.png b/theme/components/Button/__screenshots__/Button.pw.tsx_dark-color-mode_variant-ghost-dark-mode-base-view-1.png
deleted file mode 100644
index 37ff0091e6..0000000000
Binary files a/theme/components/Button/__screenshots__/Button.pw.tsx_dark-color-mode_variant-ghost-dark-mode-base-view-1.png and /dev/null differ
diff --git a/theme/components/Button/__screenshots__/Button.pw.tsx_dark-color-mode_variant-header-dark-mode-base-view-1.png b/theme/components/Button/__screenshots__/Button.pw.tsx_dark-color-mode_variant-header-dark-mode-base-view-1.png
deleted file mode 100644
index e622a8b4eb..0000000000
Binary files a/theme/components/Button/__screenshots__/Button.pw.tsx_dark-color-mode_variant-header-dark-mode-base-view-1.png and /dev/null differ
diff --git a/theme/components/Button/__screenshots__/Button.pw.tsx_dark-color-mode_variant-hero-dark-mode-base-view-1.png b/theme/components/Button/__screenshots__/Button.pw.tsx_dark-color-mode_variant-hero-dark-mode-base-view-1.png
deleted file mode 100644
index ddc8f0ef3c..0000000000
Binary files a/theme/components/Button/__screenshots__/Button.pw.tsx_dark-color-mode_variant-hero-dark-mode-base-view-1.png and /dev/null differ
diff --git a/theme/components/Button/__screenshots__/Button.pw.tsx_dark-color-mode_variant-outline-with-blue-color-scheme-dark-mode-base-view-1.png b/theme/components/Button/__screenshots__/Button.pw.tsx_dark-color-mode_variant-outline-with-blue-color-scheme-dark-mode-base-view-1.png
deleted file mode 100644
index b079eac502..0000000000
Binary files a/theme/components/Button/__screenshots__/Button.pw.tsx_dark-color-mode_variant-outline-with-blue-color-scheme-dark-mode-base-view-1.png and /dev/null differ
diff --git a/theme/components/Button/__screenshots__/Button.pw.tsx_dark-color-mode_variant-outline-with-gray-color-scheme-dark-mode-base-view-1.png b/theme/components/Button/__screenshots__/Button.pw.tsx_dark-color-mode_variant-outline-with-gray-color-scheme-dark-mode-base-view-1.png
deleted file mode 100644
index 9262b0d04a..0000000000
Binary files a/theme/components/Button/__screenshots__/Button.pw.tsx_dark-color-mode_variant-outline-with-gray-color-scheme-dark-mode-base-view-1.png and /dev/null differ
diff --git a/theme/components/Button/__screenshots__/Button.pw.tsx_dark-color-mode_variant-radio-group-dark-mode-base-view-1.png b/theme/components/Button/__screenshots__/Button.pw.tsx_dark-color-mode_variant-radio-group-dark-mode-base-view-1.png
deleted file mode 100644
index cf4a712198..0000000000
Binary files a/theme/components/Button/__screenshots__/Button.pw.tsx_dark-color-mode_variant-radio-group-dark-mode-base-view-1.png and /dev/null differ
diff --git a/theme/components/Button/__screenshots__/Button.pw.tsx_dark-color-mode_variant-simple-dark-mode-base-view-1.png b/theme/components/Button/__screenshots__/Button.pw.tsx_dark-color-mode_variant-simple-dark-mode-base-view-1.png
deleted file mode 100644
index 82c8ff359e..0000000000
Binary files a/theme/components/Button/__screenshots__/Button.pw.tsx_dark-color-mode_variant-simple-dark-mode-base-view-1.png and /dev/null differ
diff --git a/theme/components/Button/__screenshots__/Button.pw.tsx_dark-color-mode_variant-subtle-with-gray-color-scheme-dark-mode-base-view-1.png b/theme/components/Button/__screenshots__/Button.pw.tsx_dark-color-mode_variant-subtle-with-gray-color-scheme-dark-mode-base-view-1.png
deleted file mode 100644
index aedec3455a..0000000000
Binary files a/theme/components/Button/__screenshots__/Button.pw.tsx_dark-color-mode_variant-subtle-with-gray-color-scheme-dark-mode-base-view-1.png and /dev/null differ
diff --git a/theme/components/Button/__screenshots__/Button.pw.tsx_default_variant-ghost-dark-mode-base-view-1.png b/theme/components/Button/__screenshots__/Button.pw.tsx_default_variant-ghost-dark-mode-base-view-1.png
deleted file mode 100644
index c1f3c959cf..0000000000
Binary files a/theme/components/Button/__screenshots__/Button.pw.tsx_default_variant-ghost-dark-mode-base-view-1.png and /dev/null differ
diff --git a/theme/components/Button/__screenshots__/Button.pw.tsx_default_variant-header-dark-mode-base-view-1.png b/theme/components/Button/__screenshots__/Button.pw.tsx_default_variant-header-dark-mode-base-view-1.png
deleted file mode 100644
index 026d216e0a..0000000000
Binary files a/theme/components/Button/__screenshots__/Button.pw.tsx_default_variant-header-dark-mode-base-view-1.png and /dev/null differ
diff --git a/theme/components/Button/__screenshots__/Button.pw.tsx_default_variant-hero-dark-mode-base-view-1.png b/theme/components/Button/__screenshots__/Button.pw.tsx_default_variant-hero-dark-mode-base-view-1.png
deleted file mode 100644
index 5df1869f44..0000000000
Binary files a/theme/components/Button/__screenshots__/Button.pw.tsx_default_variant-hero-dark-mode-base-view-1.png and /dev/null differ
diff --git a/theme/components/Button/__screenshots__/Button.pw.tsx_default_variant-outline-with-blue-color-scheme-dark-mode-base-view-1.png b/theme/components/Button/__screenshots__/Button.pw.tsx_default_variant-outline-with-blue-color-scheme-dark-mode-base-view-1.png
deleted file mode 100644
index a3a33865d4..0000000000
Binary files a/theme/components/Button/__screenshots__/Button.pw.tsx_default_variant-outline-with-blue-color-scheme-dark-mode-base-view-1.png and /dev/null differ
diff --git a/theme/components/Button/__screenshots__/Button.pw.tsx_default_variant-outline-with-gray-color-scheme-dark-mode-base-view-1.png b/theme/components/Button/__screenshots__/Button.pw.tsx_default_variant-outline-with-gray-color-scheme-dark-mode-base-view-1.png
deleted file mode 100644
index 9d69c07b5d..0000000000
Binary files a/theme/components/Button/__screenshots__/Button.pw.tsx_default_variant-outline-with-gray-color-scheme-dark-mode-base-view-1.png and /dev/null differ
diff --git a/theme/components/Button/__screenshots__/Button.pw.tsx_default_variant-radio-group-dark-mode-base-view-1.png b/theme/components/Button/__screenshots__/Button.pw.tsx_default_variant-radio-group-dark-mode-base-view-1.png
deleted file mode 100644
index f8a1d79190..0000000000
Binary files a/theme/components/Button/__screenshots__/Button.pw.tsx_default_variant-radio-group-dark-mode-base-view-1.png and /dev/null differ
diff --git a/theme/components/Button/__screenshots__/Button.pw.tsx_default_variant-simple-dark-mode-base-view-1.png b/theme/components/Button/__screenshots__/Button.pw.tsx_default_variant-simple-dark-mode-base-view-1.png
deleted file mode 100644
index eb6bb39eb0..0000000000
Binary files a/theme/components/Button/__screenshots__/Button.pw.tsx_default_variant-simple-dark-mode-base-view-1.png and /dev/null differ
diff --git a/theme/components/Button/__screenshots__/Button.pw.tsx_default_variant-solid-base-view-1.png b/theme/components/Button/__screenshots__/Button.pw.tsx_default_variant-solid-base-view-1.png
deleted file mode 100644
index 88de692805..0000000000
Binary files a/theme/components/Button/__screenshots__/Button.pw.tsx_default_variant-solid-base-view-1.png and /dev/null differ
diff --git a/theme/components/Button/__screenshots__/Button.pw.tsx_default_variant-subtle-base-view-1.png b/theme/components/Button/__screenshots__/Button.pw.tsx_default_variant-subtle-base-view-1.png
deleted file mode 100644
index 36b02771b6..0000000000
Binary files a/theme/components/Button/__screenshots__/Button.pw.tsx_default_variant-subtle-base-view-1.png and /dev/null differ
diff --git a/theme/components/Button/__screenshots__/Button.pw.tsx_default_variant-subtle-with-gray-color-scheme-dark-mode-base-view-1.png b/theme/components/Button/__screenshots__/Button.pw.tsx_default_variant-subtle-with-gray-color-scheme-dark-mode-base-view-1.png
deleted file mode 100644
index fdecbca7b8..0000000000
Binary files a/theme/components/Button/__screenshots__/Button.pw.tsx_default_variant-subtle-with-gray-color-scheme-dark-mode-base-view-1.png and /dev/null differ
diff --git a/theme/components/Checkbox.ts b/theme/components/Checkbox.ts
deleted file mode 100644
index 7a24f98e0c..0000000000
--- a/theme/components/Checkbox.ts
+++ /dev/null
@@ -1,66 +0,0 @@
-import { checkboxAnatomy as parts } from '@chakra-ui/anatomy';
-import {
- createMultiStyleConfigHelpers,
- defineStyle,
- cssVar,
-} from '@chakra-ui/styled-system';
-import { mode } from '@chakra-ui/theme-tools';
-import { runIfFn } from '@chakra-ui/utils';
-
-const { definePartsStyle, defineMultiStyleConfig } =
- createMultiStyleConfigHelpers(parts.keys);
-
-const $size = cssVar('checkbox-size');
-
-const baseStyleControl = defineStyle((props) => {
- const { colorScheme: c } = props;
-
- return {
- _checked: {
- bg: mode(`${ c }.500`, `${ c }.300`)(props),
- borderColor: mode(`${ c }.500`, `${ c }.300`)(props),
- _hover: {
- bg: mode(`${ c }.600`, `${ c }.400`)(props),
- borderColor: mode(`${ c }.600`, `${ c }.400`)(props),
- },
- },
- _indeterminate: {
- bg: mode(`${ c }.500`, `${ c }.300`)(props),
- borderColor: mode(`${ c }.500`, `${ c }.300`)(props),
- },
- };
-});
-
-const sizes = {
- sm: definePartsStyle({
- control: { [$size.variable]: 'sizes.3' },
- label: { fontSize: 'sm' },
- icon: { fontSize: '3xs' },
- }),
- md: definePartsStyle({
- control: { [$size.variable]: 'sizes.4' },
- label: { fontSize: 'md' },
- icon: { fontSize: '2xs' },
- }),
- lg: definePartsStyle({
- control: { [$size.variable]: 'sizes.5' },
- label: { fontSize: 'md' },
- icon: { fontSize: '2xs' },
- }),
-};
-
-const baseStyleLabel = defineStyle({
- _disabled: { opacity: 0.2 },
-});
-
-const baseStyle = definePartsStyle((props) => ({
- label: baseStyleLabel,
- control: runIfFn(baseStyleControl, props),
-}));
-
-const Checkbox = defineMultiStyleConfig({
- baseStyle,
- sizes,
-});
-
-export default Checkbox;
diff --git a/theme/components/Drawer.ts b/theme/components/Drawer.ts
deleted file mode 100644
index 47ceb12b63..0000000000
--- a/theme/components/Drawer.ts
+++ /dev/null
@@ -1,45 +0,0 @@
-import { drawerAnatomy as parts } from '@chakra-ui/anatomy';
-import {
- createMultiStyleConfigHelpers,
- defineStyle,
-} from '@chakra-ui/styled-system';
-import { mode } from '@chakra-ui/theme-tools';
-import { runIfFn } from '@chakra-ui/utils';
-
-const { definePartsStyle, defineMultiStyleConfig } =
- createMultiStyleConfigHelpers(parts.keys);
-
-import getDefaultTransitionProps from '../utils/getDefaultTransitionProps';
-
-const transitionProps = getDefaultTransitionProps();
-
-const baseStyleOverlay = defineStyle({
- ...transitionProps,
- bg: 'blackAlpha.800',
- zIndex: 'overlay',
-});
-
-const baseStyleDialog = defineStyle((props) => {
- const { isFullHeight } = props;
-
- return {
- ...(isFullHeight && { height: '100vh' }),
- ...transitionProps,
- zIndex: 'modal',
- maxH: '100vh',
- bg: mode('white', 'gray.900')(props),
- color: 'inherit',
- boxShadow: mode('lg', 'dark-lg')(props),
- };
-});
-
-const baseStyle = definePartsStyle((props) => ({
- overlay: baseStyleOverlay,
- dialog: runIfFn(baseStyleDialog, props),
-}));
-
-const Drawer = defineMultiStyleConfig({
- baseStyle,
-});
-
-export default Drawer;
diff --git a/theme/components/FancySelect.ts b/theme/components/FancySelect.ts
deleted file mode 100644
index 72fd57425d..0000000000
--- a/theme/components/FancySelect.ts
+++ /dev/null
@@ -1,26 +0,0 @@
-const sizes = {
- sm: {
- field: {
- px: '0',
- height: '36px',
- },
- },
- md: {
- field: {
- px: '0',
- height: '56px',
- },
- },
- lg: {
- field: {
- px: '0',
- height: '76px',
- },
- },
-};
-
-const FancySelect = {
- sizes,
-};
-
-export default FancySelect;
diff --git a/theme/components/Form.ts b/theme/components/Form.ts
deleted file mode 100644
index 119e2b0316..0000000000
--- a/theme/components/Form.ts
+++ /dev/null
@@ -1,167 +0,0 @@
-import { formAnatomy as parts } from '@chakra-ui/anatomy';
-import { createMultiStyleConfigHelpers } from '@chakra-ui/styled-system';
-import type { StyleFunctionProps } from '@chakra-ui/theme-tools';
-
-import getFormStyles from '../utils/getFormStyles';
-import FancySelect from './FancySelect';
-import FormLabel from './FormLabel';
-import Input from './Input';
-import Textarea from './Textarea';
-
-const { definePartsStyle, defineMultiStyleConfig } =
- createMultiStyleConfigHelpers(parts.keys);
-
-function getFloatingVariantStylesForSize(size: 'md' | 'lg', props: StyleFunctionProps) {
- const formStyles = getFormStyles(props);
-
- const activeLabelStyles = {
- ...FormLabel.variants?.floating?.(props)._focusWithin,
- ...FormLabel.sizes?.[size](props)._focusWithin,
- };
-
- const activeInputStyles = (() => {
- switch (size) {
- case 'md': {
- return {
- paddingTop: '26px',
- paddingBottom: '10px',
- };
- }
-
- case 'lg': {
- return {
- paddingTop: '38px',
- paddingBottom: '18px',
- };
- }
- }
- })();
-
- const inputPx = (() => {
- switch (size) {
- case 'md': {
- return '16px';
- }
-
- case 'lg': {
- return '24px';
- }
- }
- })();
-
- return {
- container: {
- // active styles
- _focusWithin: {
- label: activeLabelStyles,
- 'input, textarea': activeInputStyles,
- },
- '&[data-active=true] label': activeLabelStyles,
-
- // label styles
- label: FormLabel.sizes?.[size](props) || {},
- 'input:not(:placeholder-shown) + label, textarea:not(:placeholder-shown) + label': activeLabelStyles,
- 'textarea:not(:placeholder-shown) + label': {
- bgColor: formStyles.input.filled.bgColor,
- },
- [`
- input[readonly] + label,
- textarea[readonly] + label,
- &[aria-readonly=true] label
- `]: {
- bgColor: formStyles.input.readOnly.bgColor,
- },
- [`
- input[aria-invalid=true] + label,
- textarea[aria-invalid=true] + label,
- &[aria-invalid=true] label
- `]: {
- color: formStyles.placeholder.error.color,
- },
- [`
- input[disabled] + label,
- textarea[disabled] + label,
- &[aria-disabled=true] label
- `]: {
- color: formStyles.placeholder.disabled.color,
- },
-
- // input styles
- input: Input.sizes?.[size].field,
- 'input[aria-autocomplete=list]': FancySelect.sizes[size].field,
- textarea: Textarea.sizes?.[size],
- 'input, textarea': {
- padding: inputPx,
- },
- 'input:not(:placeholder-shown), textarea:not(:placeholder-shown)': activeInputStyles,
-
- // indicator styles
- 'input:not(:placeholder-shown) + label .chakra-form__required-indicator, textarea:not(:placeholder-shown) + label .chakra-form__required-indicator': {
- color: formStyles.placeholder.default.color,
- },
- [`
- input[aria-invalid=true] + label .chakra-form__required-indicator,
- textarea[aria-invalid=true] + label .chakra-form__required-indicator,
- &[aria-invalid=true] .chakra-form__required-indicator
- `]: {
- color: formStyles.placeholder.error.color,
- },
- [`
- input[disabled] + label .chakra-form__required-indicator,
- textarea[disabled] + label .chakra-form__required-indicator,
- &[aria-disabled=true] .chakra-form__required-indicator
- `]: {
- color: formStyles.placeholder.disabled.color,
- },
- },
- };
-}
-
-const baseStyle = definePartsStyle(() => {
- return {
- requiredIndicator: {
- marginStart: 0,
- color: 'gray.500',
- },
- };
-});
-
-const variantFloating = definePartsStyle((props) => {
- return {
- container: {
- label: FormLabel.variants?.floating(props) || {},
- },
- };
-});
-
-const sizes = {
- lg: definePartsStyle((props) => {
- if (props.variant === 'floating') {
- return getFloatingVariantStylesForSize('lg', props);
- }
-
- return {};
- }),
- md: definePartsStyle((props) => {
- if (props.variant === 'floating') {
- return getFloatingVariantStylesForSize('md', props);
- }
-
- return {};
- }),
-};
-
-const variants = {
- floating: variantFloating,
-};
-
-const Form = defineMultiStyleConfig({
- baseStyle,
- variants,
- sizes,
- defaultProps: {
- size: 'md',
- },
-});
-
-export default Form;
diff --git a/theme/components/Form/FormControl.pw.tsx b/theme/components/Form/FormControl.pw.tsx
deleted file mode 100644
index 39ab811036..0000000000
--- a/theme/components/Form/FormControl.pw.tsx
+++ /dev/null
@@ -1,110 +0,0 @@
-import { FormControl, Input, FormLabel } from '@chakra-ui/react';
-import React from 'react';
-
-import { test, expect } from 'playwright/lib';
-
-test.use({ viewport: { width: 500, height: 300 } });
-
-test.describe('floating label size md +@dark-mode', () => {
- test('empty', async({ render }) => {
- const component = await render(
-
-
- Smart contract / Address (0x...)
- ,
- );
-
- await expect(component).toHaveScreenshot();
-
- await component.locator('input').focus();
- await expect(component).toHaveScreenshot();
- });
-
- test('empty error', async({ render }) => {
- const component = await render(
-
-
- Smart contract / Address (0x...)
- ,
- );
-
- await expect(component).toHaveScreenshot();
-
- await component.locator('input').focus();
- await expect(component).toHaveScreenshot();
- });
-
- test('filled', async({ render }) => {
- const component = await render(
-
-
- Smart contract / Address (0x...)
- ,
- );
-
- await expect(component).toHaveScreenshot();
- });
-
- test('filled disabled', async({ render }) => {
- const component = await render(
-
-
- Smart contract / Address (0x...)
- ,
- );
-
- await expect(component).toHaveScreenshot();
- });
-
- test('filled read-only', async({ render }) => {
- const component = await render(
-
-
- Smart contract / Address (0x...)
- ,
- );
-
- await expect(component).toHaveScreenshot();
- });
-
- test('filled error', async({ render }) => {
- const component = await render(
-
-
- Smart contract / Address (0x...)
- ,
- );
-
- await expect(component).toHaveScreenshot();
- });
-});
-
-test.describe('floating label size lg +@dark-mode', () => {
- test('empty', async({ render }) => {
- const component = await render(
-
-
- Smart contract / Address (0x...)
- ,
- );
-
- await expect(component).toHaveScreenshot();
-
- await component.locator('input').focus();
- await expect(component).toHaveScreenshot();
- });
-
- test('filled', async({ render }) => {
- const component = await render(
-
-
- Smart contract / Address (0x...)
- ,
- );
-
- await expect(component).toHaveScreenshot();
-
- await component.locator('input').focus();
- await expect(component).toHaveScreenshot();
- });
-});
diff --git a/theme/components/Form/__screenshots__/FormControl.pw.tsx_dark-color-mode_floating-label-size-lg-dark-mode-empty-1.png b/theme/components/Form/__screenshots__/FormControl.pw.tsx_dark-color-mode_floating-label-size-lg-dark-mode-empty-1.png
deleted file mode 100644
index 1f1dd2e377..0000000000
Binary files a/theme/components/Form/__screenshots__/FormControl.pw.tsx_dark-color-mode_floating-label-size-lg-dark-mode-empty-1.png and /dev/null differ
diff --git a/theme/components/Form/__screenshots__/FormControl.pw.tsx_dark-color-mode_floating-label-size-lg-dark-mode-empty-2.png b/theme/components/Form/__screenshots__/FormControl.pw.tsx_dark-color-mode_floating-label-size-lg-dark-mode-empty-2.png
deleted file mode 100644
index 89db3e3de7..0000000000
Binary files a/theme/components/Form/__screenshots__/FormControl.pw.tsx_dark-color-mode_floating-label-size-lg-dark-mode-empty-2.png and /dev/null differ
diff --git a/theme/components/Form/__screenshots__/FormControl.pw.tsx_dark-color-mode_floating-label-size-lg-dark-mode-filled-1.png b/theme/components/Form/__screenshots__/FormControl.pw.tsx_dark-color-mode_floating-label-size-lg-dark-mode-filled-1.png
deleted file mode 100644
index ff66b3eb82..0000000000
Binary files a/theme/components/Form/__screenshots__/FormControl.pw.tsx_dark-color-mode_floating-label-size-lg-dark-mode-filled-1.png and /dev/null differ
diff --git a/theme/components/Form/__screenshots__/FormControl.pw.tsx_dark-color-mode_floating-label-size-lg-dark-mode-filled-2.png b/theme/components/Form/__screenshots__/FormControl.pw.tsx_dark-color-mode_floating-label-size-lg-dark-mode-filled-2.png
deleted file mode 100644
index c56bd9edeb..0000000000
Binary files a/theme/components/Form/__screenshots__/FormControl.pw.tsx_dark-color-mode_floating-label-size-lg-dark-mode-filled-2.png and /dev/null differ
diff --git a/theme/components/Form/__screenshots__/FormControl.pw.tsx_dark-color-mode_floating-label-size-md-dark-mode-empty-1.png b/theme/components/Form/__screenshots__/FormControl.pw.tsx_dark-color-mode_floating-label-size-md-dark-mode-empty-1.png
deleted file mode 100644
index 3453be33b4..0000000000
Binary files a/theme/components/Form/__screenshots__/FormControl.pw.tsx_dark-color-mode_floating-label-size-md-dark-mode-empty-1.png and /dev/null differ
diff --git a/theme/components/Form/__screenshots__/FormControl.pw.tsx_dark-color-mode_floating-label-size-md-dark-mode-empty-2.png b/theme/components/Form/__screenshots__/FormControl.pw.tsx_dark-color-mode_floating-label-size-md-dark-mode-empty-2.png
deleted file mode 100644
index df1f13adc2..0000000000
Binary files a/theme/components/Form/__screenshots__/FormControl.pw.tsx_dark-color-mode_floating-label-size-md-dark-mode-empty-2.png and /dev/null differ
diff --git a/theme/components/Form/__screenshots__/FormControl.pw.tsx_dark-color-mode_floating-label-size-md-dark-mode-empty-error-1.png b/theme/components/Form/__screenshots__/FormControl.pw.tsx_dark-color-mode_floating-label-size-md-dark-mode-empty-error-1.png
deleted file mode 100644
index 4d19167035..0000000000
Binary files a/theme/components/Form/__screenshots__/FormControl.pw.tsx_dark-color-mode_floating-label-size-md-dark-mode-empty-error-1.png and /dev/null differ
diff --git a/theme/components/Form/__screenshots__/FormControl.pw.tsx_dark-color-mode_floating-label-size-md-dark-mode-empty-error-2.png b/theme/components/Form/__screenshots__/FormControl.pw.tsx_dark-color-mode_floating-label-size-md-dark-mode-empty-error-2.png
deleted file mode 100644
index 8f252b3add..0000000000
Binary files a/theme/components/Form/__screenshots__/FormControl.pw.tsx_dark-color-mode_floating-label-size-md-dark-mode-empty-error-2.png and /dev/null differ
diff --git a/theme/components/Form/__screenshots__/FormControl.pw.tsx_dark-color-mode_floating-label-size-md-dark-mode-filled-1.png b/theme/components/Form/__screenshots__/FormControl.pw.tsx_dark-color-mode_floating-label-size-md-dark-mode-filled-1.png
deleted file mode 100644
index d1c5826ade..0000000000
Binary files a/theme/components/Form/__screenshots__/FormControl.pw.tsx_dark-color-mode_floating-label-size-md-dark-mode-filled-1.png and /dev/null differ
diff --git a/theme/components/Form/__screenshots__/FormControl.pw.tsx_dark-color-mode_floating-label-size-md-dark-mode-filled-disabled-1.png b/theme/components/Form/__screenshots__/FormControl.pw.tsx_dark-color-mode_floating-label-size-md-dark-mode-filled-disabled-1.png
deleted file mode 100644
index c37d9af17f..0000000000
Binary files a/theme/components/Form/__screenshots__/FormControl.pw.tsx_dark-color-mode_floating-label-size-md-dark-mode-filled-disabled-1.png and /dev/null differ
diff --git a/theme/components/Form/__screenshots__/FormControl.pw.tsx_dark-color-mode_floating-label-size-md-dark-mode-filled-error-1.png b/theme/components/Form/__screenshots__/FormControl.pw.tsx_dark-color-mode_floating-label-size-md-dark-mode-filled-error-1.png
deleted file mode 100644
index 36af890044..0000000000
Binary files a/theme/components/Form/__screenshots__/FormControl.pw.tsx_dark-color-mode_floating-label-size-md-dark-mode-filled-error-1.png and /dev/null differ
diff --git a/theme/components/Form/__screenshots__/FormControl.pw.tsx_dark-color-mode_floating-label-size-md-dark-mode-filled-read-only-1.png b/theme/components/Form/__screenshots__/FormControl.pw.tsx_dark-color-mode_floating-label-size-md-dark-mode-filled-read-only-1.png
deleted file mode 100644
index 06974bcc7c..0000000000
Binary files a/theme/components/Form/__screenshots__/FormControl.pw.tsx_dark-color-mode_floating-label-size-md-dark-mode-filled-read-only-1.png and /dev/null differ
diff --git a/theme/components/Form/__screenshots__/FormControl.pw.tsx_default_floating-label-size-lg-dark-mode-empty-1.png b/theme/components/Form/__screenshots__/FormControl.pw.tsx_default_floating-label-size-lg-dark-mode-empty-1.png
deleted file mode 100644
index eea73198d2..0000000000
Binary files a/theme/components/Form/__screenshots__/FormControl.pw.tsx_default_floating-label-size-lg-dark-mode-empty-1.png and /dev/null differ
diff --git a/theme/components/Form/__screenshots__/FormControl.pw.tsx_default_floating-label-size-lg-dark-mode-empty-2.png b/theme/components/Form/__screenshots__/FormControl.pw.tsx_default_floating-label-size-lg-dark-mode-empty-2.png
deleted file mode 100644
index 60812d7443..0000000000
Binary files a/theme/components/Form/__screenshots__/FormControl.pw.tsx_default_floating-label-size-lg-dark-mode-empty-2.png and /dev/null differ
diff --git a/theme/components/Form/__screenshots__/FormControl.pw.tsx_default_floating-label-size-lg-dark-mode-filled-1.png b/theme/components/Form/__screenshots__/FormControl.pw.tsx_default_floating-label-size-lg-dark-mode-filled-1.png
deleted file mode 100644
index 24de73349c..0000000000
Binary files a/theme/components/Form/__screenshots__/FormControl.pw.tsx_default_floating-label-size-lg-dark-mode-filled-1.png and /dev/null differ
diff --git a/theme/components/Form/__screenshots__/FormControl.pw.tsx_default_floating-label-size-lg-dark-mode-filled-2.png b/theme/components/Form/__screenshots__/FormControl.pw.tsx_default_floating-label-size-lg-dark-mode-filled-2.png
deleted file mode 100644
index 0f549d03d9..0000000000
Binary files a/theme/components/Form/__screenshots__/FormControl.pw.tsx_default_floating-label-size-lg-dark-mode-filled-2.png and /dev/null differ
diff --git a/theme/components/Form/__screenshots__/FormControl.pw.tsx_default_floating-label-size-md-dark-mode-empty-1.png b/theme/components/Form/__screenshots__/FormControl.pw.tsx_default_floating-label-size-md-dark-mode-empty-1.png
deleted file mode 100644
index 6c295197f2..0000000000
Binary files a/theme/components/Form/__screenshots__/FormControl.pw.tsx_default_floating-label-size-md-dark-mode-empty-1.png and /dev/null differ
diff --git a/theme/components/Form/__screenshots__/FormControl.pw.tsx_default_floating-label-size-md-dark-mode-empty-2.png b/theme/components/Form/__screenshots__/FormControl.pw.tsx_default_floating-label-size-md-dark-mode-empty-2.png
deleted file mode 100644
index e118b2e86f..0000000000
Binary files a/theme/components/Form/__screenshots__/FormControl.pw.tsx_default_floating-label-size-md-dark-mode-empty-2.png and /dev/null differ
diff --git a/theme/components/Form/__screenshots__/FormControl.pw.tsx_default_floating-label-size-md-dark-mode-empty-error-1.png b/theme/components/Form/__screenshots__/FormControl.pw.tsx_default_floating-label-size-md-dark-mode-empty-error-1.png
deleted file mode 100644
index 84482f53d7..0000000000
Binary files a/theme/components/Form/__screenshots__/FormControl.pw.tsx_default_floating-label-size-md-dark-mode-empty-error-1.png and /dev/null differ
diff --git a/theme/components/Form/__screenshots__/FormControl.pw.tsx_default_floating-label-size-md-dark-mode-empty-error-2.png b/theme/components/Form/__screenshots__/FormControl.pw.tsx_default_floating-label-size-md-dark-mode-empty-error-2.png
deleted file mode 100644
index b077743f2f..0000000000
Binary files a/theme/components/Form/__screenshots__/FormControl.pw.tsx_default_floating-label-size-md-dark-mode-empty-error-2.png and /dev/null differ
diff --git a/theme/components/Form/__screenshots__/FormControl.pw.tsx_default_floating-label-size-md-dark-mode-filled-1.png b/theme/components/Form/__screenshots__/FormControl.pw.tsx_default_floating-label-size-md-dark-mode-filled-1.png
deleted file mode 100644
index 56712ddcf5..0000000000
Binary files a/theme/components/Form/__screenshots__/FormControl.pw.tsx_default_floating-label-size-md-dark-mode-filled-1.png and /dev/null differ
diff --git a/theme/components/Form/__screenshots__/FormControl.pw.tsx_default_floating-label-size-md-dark-mode-filled-disabled-1.png b/theme/components/Form/__screenshots__/FormControl.pw.tsx_default_floating-label-size-md-dark-mode-filled-disabled-1.png
deleted file mode 100644
index 9b30da40a5..0000000000
Binary files a/theme/components/Form/__screenshots__/FormControl.pw.tsx_default_floating-label-size-md-dark-mode-filled-disabled-1.png and /dev/null differ
diff --git a/theme/components/Form/__screenshots__/FormControl.pw.tsx_default_floating-label-size-md-dark-mode-filled-error-1.png b/theme/components/Form/__screenshots__/FormControl.pw.tsx_default_floating-label-size-md-dark-mode-filled-error-1.png
deleted file mode 100644
index acb481c7a7..0000000000
Binary files a/theme/components/Form/__screenshots__/FormControl.pw.tsx_default_floating-label-size-md-dark-mode-filled-error-1.png and /dev/null differ
diff --git a/theme/components/Form/__screenshots__/FormControl.pw.tsx_default_floating-label-size-md-dark-mode-filled-read-only-1.png b/theme/components/Form/__screenshots__/FormControl.pw.tsx_default_floating-label-size-md-dark-mode-filled-read-only-1.png
deleted file mode 100644
index c721e5196e..0000000000
Binary files a/theme/components/Form/__screenshots__/FormControl.pw.tsx_default_floating-label-size-md-dark-mode-filled-read-only-1.png and /dev/null differ
diff --git a/theme/components/FormLabel.ts b/theme/components/FormLabel.ts
deleted file mode 100644
index c67ac969eb..0000000000
--- a/theme/components/FormLabel.ts
+++ /dev/null
@@ -1,109 +0,0 @@
-import { defineStyle, defineStyleConfig } from '@chakra-ui/styled-system';
-
-import getFormStyles from '../utils/getFormStyles';
-
-const baseStyle = defineStyle({
- display: 'flex',
- fontSize: 'md',
- marginEnd: '3',
- mb: '2',
- fontWeight: 'medium',
- transitionProperty: 'common',
- transitionDuration: 'normal',
- opacity: 1,
- _disabled: {
- opacity: 0.2,
- },
-});
-
-const variantFloating = defineStyle((props) => {
- const formStyles = getFormStyles(props);
-
- return {
- left: '2px',
- top: '2px',
- zIndex: 2,
- position: 'absolute',
- borderRadius: 'base',
- boxSizing: 'border-box',
- color: formStyles.placeholder.default.color,
- backgroundColor: props.bgColor || props.backgroundColor || 'transparent',
- pointerEvents: 'none',
- margin: 0,
- transformOrigin: 'top left',
- transitionProperty: 'font-size, line-height, padding, top, background-color',
- overflow: 'hidden',
- whiteSpace: 'nowrap',
- textOverflow: 'ellipsis',
- _focusWithin: {
- backgroundColor: props.bgColor || props.backgroundColor || 'transparent',
- color: formStyles.placeholder.default.color,
- fontSize: 'xs',
- lineHeight: '16px',
- borderTopRightRadius: 'none',
- '& svg': {
- width: '16px',
- height: '16px',
- },
- },
- '& svg': {
- transitionProperty: 'width, height',
- transitionDuration: 'normal',
- transitionTimingFunction: 'ease',
- width: '24px',
- height: '24px',
- mr: '2',
- },
- };
-});
-
-const variants = {
- floating: variantFloating,
-};
-
-const sizes = {
- lg: defineStyle((props) => {
- if (props.variant === 'floating') {
- return {
- fontSize: 'md',
- lineHeight: '24px',
- padding: '26px 4px 26px 24px',
- right: '22px',
- _focusWithin: {
- padding: '16px 0 2px 24px',
- },
- '&[data-fancy=true]': {
- right: '36px',
- },
- };
- }
-
- return {};
- }),
- md: defineStyle((props) => {
- if (props.variant === 'floating') {
- return {
- fontSize: 'md',
- lineHeight: '20px',
- padding: '18px 4px 18px 16px',
- right: '22px',
- _focusWithin: {
- padding: '10px 0 2px 16px',
- },
- '&[data-fancy=true]': {
- right: '36px',
- },
- };
- }
-
- return {};
- }),
-};
-
-const FormLabel = defineStyleConfig({
- variants,
- baseStyle,
- sizes,
-});
-
-export default FormLabel;
diff --git a/theme/components/Heading.ts b/theme/components/Heading.ts
deleted file mode 100644
index e9cf8c51b2..0000000000
--- a/theme/components/Heading.ts
+++ /dev/null
@@ -1,45 +0,0 @@
-import { defineStyle, defineStyleConfig } from '@chakra-ui/styled-system';
-import type { SystemStyleInterpolation } from '@chakra-ui/theme-tools';
-import { mode } from '@chakra-ui/theme-tools';
-
-const baseStyle: SystemStyleInterpolation = (props) => {
- return {
- fontWeight: '500',
- color: mode('blackAlpha.800', 'whiteAlpha.800')(props),
- };
-};
-
-const sizes = {
- '2xl': defineStyle({
- fontSize: '48px',
- lineHeight: '60px',
- }),
- xl: defineStyle({
- fontSize: '40px',
- lineHeight: '48px',
- }),
- lg: defineStyle({
- fontSize: '32px',
- lineHeight: '40px',
- letterSpacing: '-0.5px',
- }),
- md: defineStyle({
- fontSize: '24px',
- lineHeight: '32px',
- }),
- sm: defineStyle({
- fontSize: '18px',
- lineHeight: '24px',
- }),
- xs: defineStyle({
- fontSize: '14px',
- lineHeight: '20px',
- }),
-};
-
-const Heading = defineStyleConfig({
- sizes,
- baseStyle,
-});
-
-export default Heading;
diff --git a/theme/components/Input.ts b/theme/components/Input.ts
deleted file mode 100644
index d127835109..0000000000
--- a/theme/components/Input.ts
+++ /dev/null
@@ -1,119 +0,0 @@
-import { inputAnatomy as parts } from '@chakra-ui/anatomy';
-import { Input as InputComponent } from '@chakra-ui/react';
-import {
- createMultiStyleConfigHelpers,
- defineStyle,
-} from '@chakra-ui/styled-system';
-import { mode } from '@chakra-ui/theme-tools';
-
-const { definePartsStyle, defineMultiStyleConfig } =
- createMultiStyleConfigHelpers(parts.keys);
-
-import getDefaultTransitionProps from '../utils/getDefaultTransitionProps';
-import getOutlinedFieldStyles from '../utils/getOutlinedFieldStyles';
-
-const size = {
- xs: defineStyle({
- fontSize: 'md',
- lineHeight: '24px',
- px: '8px',
- py: '4px',
- h: '32px',
- borderRadius: 'base',
- }),
- sm: defineStyle({
- fontSize: 'md',
- lineHeight: '24px',
- px: '8px',
- py: '12px',
- h: '40px',
- borderRadius: 'base',
- }),
- // TEMPORARY INPUT SIZE!!!
- // soon we will migrate to the new size and get rid off this one
- // lg -> 60
- // md -> 48
- // sm -> 40
- // xs ->32
- sm_md: defineStyle({
- fontSize: 'md',
- lineHeight: '24px',
- px: '8px',
- py: '12px',
- h: '48px',
- borderRadius: 'base',
- }),
- md: defineStyle({
- fontSize: 'md',
- lineHeight: '20px',
- px: '20px',
- py: '20px',
- h: '60px',
- borderRadius: 'base',
- }),
- lg: defineStyle({
- fontSize: 'md',
- lineHeight: '20px',
- px: '24px',
- py: '28px',
- h: '80px',
- borderRadius: 'base',
- }),
-};
-
-const variantOutline = definePartsStyle((props) => {
- const transitionProps = getDefaultTransitionProps();
-
- return {
- field: getOutlinedFieldStyles(props),
- addon: {
- border: '2px solid',
- borderColor: 'transparent',
- bg: mode('blackAlpha.100', 'whiteAlpha.200')(props),
- color: mode('blackAlpha.800', 'whiteAlpha.800')(props),
- ...transitionProps,
- },
- };
-});
-
-const sizes = {
- xs: definePartsStyle({
- field: size.xs,
- addon: size.xs,
- }),
- sm: definePartsStyle({
- field: size.sm,
- addon: size.sm,
- }),
- sm_md: definePartsStyle({
- field: size.sm_md,
- addon: size.sm_md,
- }),
- md: definePartsStyle({
- field: size.md,
- addon: size.md,
- }),
- lg: definePartsStyle({
- field: size.lg,
- addon: size.lg,
- }),
-};
-
-const variants = {
- outline: variantOutline,
-};
-
-const Input = defineMultiStyleConfig({
- sizes,
- variants,
- defaultProps: {
- size: 'md',
- },
-});
-
-InputComponent.defaultProps = {
- ...InputComponent.defaultProps,
- placeholder: ' ',
-};
-
-export default Input;
diff --git a/theme/components/Link.ts b/theme/components/Link.ts
deleted file mode 100644
index 6cbee7bf00..0000000000
--- a/theme/components/Link.ts
+++ /dev/null
@@ -1,43 +0,0 @@
-import type { SystemStyleInterpolation } from '@chakra-ui/styled-system';
-import { defineStyle, defineStyleConfig } from '@chakra-ui/styled-system';
-import { mode } from '@chakra-ui/theme-tools';
-
-import getDefaultTransitionProps from '../utils/getDefaultTransitionProps';
-
-const baseStyle = defineStyle(getDefaultTransitionProps());
-
-const variantPrimary = defineStyle((props) => {
- return {
- color: 'link',
- _hover: {
- color: 'link_hovered',
- textDecorationStyle: props.textDecorationStyle || 'solid',
- },
- };
-});
-
-const variantSecondary = defineStyle((props) => {
- return {
- color: mode('gray.600', 'gray.500')(props),
- _hover: {
- color: mode('gray.600', 'gray.400')(props),
- },
- };
-});
-
-const variants: Record = {
- primary: variantPrimary,
- secondary: variantSecondary,
-};
-
-const defaultProps = {
- variant: 'primary',
-};
-
-const Link = defineStyleConfig({
- variants,
- defaultProps,
- baseStyle,
-});
-
-export default Link;
diff --git a/theme/components/Menu.ts b/theme/components/Menu.ts
deleted file mode 100644
index 5e425ff738..0000000000
--- a/theme/components/Menu.ts
+++ /dev/null
@@ -1,51 +0,0 @@
-import { menuAnatomy as parts } from '@chakra-ui/anatomy';
-import {
- createMultiStyleConfigHelpers,
- cssVar,
- defineStyle,
-} from '@chakra-ui/styled-system';
-
-const { defineMultiStyleConfig, definePartsStyle } =
- createMultiStyleConfigHelpers(parts.keys);
-
-const $bg = cssVar('menu-bg');
-const $shadow = cssVar('menu-shadow');
-
-const baseStyleList = defineStyle({
- [$bg.variable]: '#fff',
- [$shadow.variable]: 'shadows.2xl',
- _dark: {
- [$bg.variable]: 'colors.gray.900',
- [$shadow.variable]: 'shadows.dark-lg',
- },
- borderWidth: '0',
- bg: $bg.reference,
- boxShadow: $shadow.reference,
-});
-
-const baseStyleItem = defineStyle({
- _focus: {
- [$bg.variable]: 'transparent',
- _dark: {
- [$bg.variable]: 'transparent',
- },
- },
- _hover: {
- [$bg.variable]: 'colors.blue.50',
- _dark: {
- [$bg.variable]: 'colors.whiteAlpha.100',
- },
- },
- bg: $bg.reference,
-});
-
-const baseStyle = definePartsStyle({
- list: baseStyleList,
- item: baseStyleItem,
-});
-
-const Menu = defineMultiStyleConfig({
- baseStyle,
-});
-
-export default Menu;
diff --git a/theme/components/Modal.ts b/theme/components/Modal.ts
deleted file mode 100644
index 2992f4e7d8..0000000000
--- a/theme/components/Modal.ts
+++ /dev/null
@@ -1,125 +0,0 @@
-import { modalAnatomy as parts } from '@chakra-ui/anatomy';
-import { Modal as ModalComponent } from '@chakra-ui/react';
-import {
- createMultiStyleConfigHelpers,
- defineStyle,
-} from '@chakra-ui/styled-system';
-import { mode } from '@chakra-ui/theme-tools';
-import { runIfFn } from '@chakra-ui/utils';
-
-const { defineMultiStyleConfig, definePartsStyle } =
- createMultiStyleConfigHelpers(parts.keys);
-
-const baseStyleDialog = defineStyle(() => {
- return {
- padding: 8,
- borderRadius: 'lg',
- bg: 'dialog_bg',
- margin: 'auto',
- };
-});
-
-const baseStyleDialogContainer = defineStyle({
- '::-webkit-scrollbar': { display: 'none' },
- 'scrollbar-width': 'none',
- // '@supports (height: -webkit-fill-available)': { height: '-webkit-fill-available' },
-});
-
-const baseStyleHeader = defineStyle((props) => ({
- padding: 0,
- marginBottom: 8,
- fontSize: '2xl',
- lineHeight: 10,
- color: mode('blackAlpha.800', 'whiteAlpha.800')(props),
-}));
-
-const baseStyleBody = defineStyle({
- padding: 0,
- marginBottom: 8,
- flex: 'initial',
-});
-
-const baseStyleFooter = defineStyle({
- padding: 0,
- justifyContent: 'flex-start',
-});
-
-const baseStyleCloseButton = defineStyle((props) => {
- return {
- top: 8,
- right: 8,
- height: 10,
- width: 10,
- color: mode('gray.700', 'gray.500')(props),
- _hover: { color: 'link_hovered' },
- _active: { bg: 'none' },
- };
-});
-
-const baseStyleOverlay = defineStyle({
- bg: 'blackAlpha.800',
-});
-
-const baseStyle = definePartsStyle((props) => ({
- dialog: runIfFn(baseStyleDialog),
- dialogContainer: baseStyleDialogContainer,
-
- header: runIfFn(baseStyleHeader, props),
- body: baseStyleBody,
- footer: baseStyleFooter,
- closeButton: runIfFn(baseStyleCloseButton, props),
- overlay: baseStyleOverlay,
-}));
-
-const sizes = {
- sm: definePartsStyle({
- dialogContainer: {
- height: '100%',
- },
- dialog: {
- maxW: '536px',
- },
- }),
- md: definePartsStyle({
- dialogContainer: {
- height: '100%',
- },
- dialog: {
- maxW: '760px',
- },
- }),
- full: definePartsStyle({
- dialogContainer: {
- height: '100%',
- },
- dialog: {
- maxW: '100vw',
- my: '0',
- borderRadius: '0',
- padding: '80px 16px 32px 16px',
- height: '100%',
- overflowY: 'scroll',
- },
- closeButton: {
- top: 4,
- right: 6,
- width: 6,
- height: 6,
- },
- header: {
- mb: 6,
- },
- }),
-};
-
-const Modal = defineMultiStyleConfig({
- sizes,
- baseStyle,
-});
-
-export default Modal;
-
-ModalComponent.defaultProps = {
- ...ModalComponent.defaultProps,
- isCentered: true,
-};
diff --git a/theme/components/PinInput.ts b/theme/components/PinInput.ts
deleted file mode 100644
index 251b38ca78..0000000000
--- a/theme/components/PinInput.ts
+++ /dev/null
@@ -1,34 +0,0 @@
-import { defineStyle, defineStyleConfig } from '@chakra-ui/styled-system';
-
-import getOutlinedFieldStyles from '../utils/getOutlinedFieldStyles';
-
-const baseStyle = defineStyle({
- textAlign: 'center',
- bgColor: 'dialog_bg',
-});
-
-const sizes = {
- md: defineStyle({
- fontSize: 'md',
- w: 10,
- h: 10,
- borderRadius: 'md',
- }),
-};
-
-const variants = {
- outline: defineStyle(
- (props) => getOutlinedFieldStyles(props),
- ),
-};
-
-const PinInput = defineStyleConfig({
- baseStyle,
- sizes,
- variants,
- defaultProps: {
- size: 'md',
- },
-});
-
-export default PinInput;
diff --git a/theme/components/Popover.ts b/theme/components/Popover.ts
deleted file mode 100644
index 9f277d8605..0000000000
--- a/theme/components/Popover.ts
+++ /dev/null
@@ -1,86 +0,0 @@
-import { popoverAnatomy as parts } from '@chakra-ui/anatomy';
-import {
- createMultiStyleConfigHelpers,
- defineStyle,
-} from '@chakra-ui/styled-system';
-import { cssVar, mode } from '@chakra-ui/theme-tools';
-
-const { defineMultiStyleConfig, definePartsStyle } =
- createMultiStyleConfigHelpers(parts.keys);
-
-const $popperBg = cssVar('popper-bg');
-
-const $arrowBg = cssVar('popper-arrow-bg');
-const $arrowShadowColor = cssVar('popper-arrow-shadow-color');
-
-const baseStylePopper = defineStyle({
- zIndex: 'popover',
-});
-
-const baseStyleContent = defineStyle((props) => {
- const bg = mode('white', 'gray.900')(props);
- const shadowColor = mode('blackAlpha.200', 'whiteAlpha.300')(props);
-
- return {
- [$popperBg.variable]: `colors.${ bg }`,
- bg: $popperBg.reference,
- [$arrowBg.variable]: $popperBg.reference,
- [$arrowShadowColor.variable]: `colors.${ shadowColor }`,
- _dark: {
- [$popperBg.variable]: `colors.gray.900`,
- [$arrowShadowColor.variable]: `colors.whiteAlpha.300`,
- boxShadow: 'dark-lg',
- },
- width: 'xs',
- border: 'none',
- borderColor: 'inherit',
- borderRadius: 'md',
- boxShadow: '2xl',
- zIndex: 'inherit',
- _focusVisible: {
- outline: 0,
- boxShadow: '2xl',
- },
- };
-});
-
-const baseStyleHeader = defineStyle({
- px: 3,
- py: 2,
- borderBottomWidth: '1px',
-});
-
-const baseStyleBody = defineStyle({
- px: 4,
- py: 4,
-});
-
-const baseStyleFooter = defineStyle({
- px: 3,
- py: 2,
- borderTopWidth: '1px',
-});
-
-const baseStyleCloseButton = defineStyle({
- position: 'absolute',
- borderRadius: 'md',
- top: 1,
- insetEnd: 2,
- padding: 2,
-});
-
-const baseStyle = definePartsStyle((props) => ({
- popper: baseStylePopper,
- content: baseStyleContent(props),
- header: baseStyleHeader,
- body: baseStyleBody,
- footer: baseStyleFooter,
- arrow: {},
- closeButton: baseStyleCloseButton,
-}));
-
-const Popover = defineMultiStyleConfig({
- baseStyle,
-});
-
-export default Popover;
diff --git a/theme/components/Radio.ts b/theme/components/Radio.ts
deleted file mode 100644
index 18033bc5fd..0000000000
--- a/theme/components/Radio.ts
+++ /dev/null
@@ -1,44 +0,0 @@
-import { radioAnatomy as parts } from '@chakra-ui/anatomy';
-import {
- createMultiStyleConfigHelpers,
- defineStyle,
-} from '@chakra-ui/styled-system';
-
-const { defineMultiStyleConfig, definePartsStyle } =
- createMultiStyleConfigHelpers(parts.keys);
-
-const baseStyleLabel = defineStyle({
- _disabled: { opacity: 0.2 },
- width: 'fit-content',
-});
-
-const baseStyleContainer = defineStyle({
- width: 'fit-content',
-});
-
-const baseStyle = definePartsStyle({
- label: baseStyleLabel,
- container: baseStyleContainer,
-});
-
-const sizes = {
- md: definePartsStyle({
- control: { w: '4', h: '4' },
- label: { fontSize: 'md' },
- }),
- lg: definePartsStyle({
- control: { w: '5', h: '5' },
- label: { fontSize: 'md' },
- }),
- sm: definePartsStyle({
- control: { width: '3', height: '3' },
- label: { fontSize: 'sm' },
- }),
-};
-
-const Radio = defineMultiStyleConfig({
- baseStyle,
- sizes,
-});
-
-export default Radio;
diff --git a/theme/components/Select.ts b/theme/components/Select.ts
deleted file mode 100644
index 3e53c6952a..0000000000
--- a/theme/components/Select.ts
+++ /dev/null
@@ -1,78 +0,0 @@
-import { selectAnatomy as parts } from '@chakra-ui/anatomy';
-import {
- createMultiStyleConfigHelpers,
- defineStyle,
-} from '@chakra-ui/styled-system';
-import { mode } from '@chakra-ui/theme-tools';
-
-import Input from './Input';
-
-const { defineMultiStyleConfig, definePartsStyle } =
- createMultiStyleConfigHelpers(parts.keys);
-
-const variantOutline = definePartsStyle((props) => {
- return {
- field: {
- ...Input.variants?.outline(props).field,
- borderColor: mode('gray.200', 'gray.600')(props),
- _hover: {
- borderColor: mode('gray.300', 'gray.500')(props),
- },
- _focusVisible: {
- borderColor: mode('gray.200', 'gray.600')(props),
- boxShadow: 'none',
- },
- cursor: 'pointer',
- },
- };
-});
-
-const iconSpacing = defineStyle({
- paddingInlineEnd: '8',
-});
-
-const sizes = {
- lg: {
- ...Input.sizes?.lg,
- field: {
- ...Input.sizes?.lg.field,
- ...iconSpacing,
- },
- },
- md: {
- ...Input.sizes?.md,
- field: {
- ...Input.sizes?.md.field,
- ...iconSpacing,
- },
- },
- sm: {
- ...Input.sizes?.sm,
- field: {
- ...Input.sizes?.sm.field,
- ...iconSpacing,
- },
- },
- xs: {
- ...Input.sizes?.xs,
- field: {
- ...Input.sizes?.xs.field,
- ...iconSpacing,
- fontSize: 'sm',
- lineHeight: '20px',
- },
- },
-};
-
-const Select = defineMultiStyleConfig({
- variants: {
- ...Input.variants,
- outline: variantOutline,
- },
- sizes,
- defaultProps: {
- size: 'xs',
- },
-});
-
-export default Select;
diff --git a/theme/components/Skeleton.ts b/theme/components/Skeleton.ts
deleted file mode 100644
index 6648bf4368..0000000000
--- a/theme/components/Skeleton.ts
+++ /dev/null
@@ -1,47 +0,0 @@
-// eslint-disable-next-line no-restricted-imports
-import { Skeleton as SkeletonComponent } from '@chakra-ui/react';
-import {
- defineStyle,
- defineStyleConfig,
-} from '@chakra-ui/styled-system';
-import { keyframes } from '@chakra-ui/system';
-import { getColor, mode } from '@chakra-ui/theme-tools';
-
-const shine = () =>
- keyframes({
- to: { backgroundPositionX: '-200%' },
- });
-
-const baseStyle = defineStyle((props) => {
- const defaultStartColor = mode('blackAlpha.50', 'whiteAlpha.50')(props);
- const defaultEndColor = mode('blackAlpha.100', 'whiteAlpha.100')(props);
-
- const {
- startColor = defaultStartColor,
- endColor = defaultEndColor,
- theme,
- } = props;
-
- const start = getColor(theme, startColor);
- const end = getColor(theme, endColor);
-
- return {
- opacity: 1,
- borderRadius: 'md',
- borderColor: start,
- background: `linear-gradient(90deg, ${ start } 8%, ${ end } 18%, ${ start } 33%)`,
- backgroundSize: '200% 100%',
- };
-});
-
-const Skeleton = defineStyleConfig({
- baseStyle,
-});
-
-export default Skeleton;
-
-SkeletonComponent.defaultProps = {
- ...SkeletonComponent.defaultProps,
- speed: 1,
- animation: `1s linear infinite ${ shine() }`,
-};
diff --git a/theme/components/Spinner.ts b/theme/components/Spinner.ts
deleted file mode 100644
index 3179c944b3..0000000000
--- a/theme/components/Spinner.ts
+++ /dev/null
@@ -1,21 +0,0 @@
-import { defineStyle, defineStyleConfig } from '@chakra-ui/styled-system';
-import { mode } from '@chakra-ui/theme-tools';
-
-const baseStyle = defineStyle((props) => {
- const { emptyColor, color } = props;
-
- return {
- borderColor: color || 'blue.500',
- borderBottomColor: emptyColor || mode('blackAlpha.200', 'whiteAlpha.200')(props),
- borderLeftColor: emptyColor || mode('blackAlpha.200', 'whiteAlpha.200')(props),
- };
-});
-
-const Spinner = defineStyleConfig({
- baseStyle,
- defaultProps: {
- size: 'md',
- },
-});
-
-export default Spinner;
diff --git a/theme/components/Switch.ts b/theme/components/Switch.ts
deleted file mode 100644
index 6059618c0f..0000000000
--- a/theme/components/Switch.ts
+++ /dev/null
@@ -1,32 +0,0 @@
-import { switchAnatomy as parts } from '@chakra-ui/anatomy';
-import { defineStyle, createMultiStyleConfigHelpers } from '@chakra-ui/styled-system';
-import { mode } from '@chakra-ui/theme-tools';
-
-const { defineMultiStyleConfig, definePartsStyle } =
- createMultiStyleConfigHelpers(parts.keys);
-
-const baseStyleTrack = defineStyle((props) => {
- const { colorScheme: c } = props;
-
- return {
- _checked: {
- bg: mode(`${ c }.500`, `${ c }.300`)(props),
- _hover: {
- bg: mode(`${ c }.600`, `${ c }.400`)(props),
- },
- },
- _focusVisible: {
- boxShadow: 'none',
- },
- };
-});
-
-const baseStyle = definePartsStyle((props) => ({
- track: baseStyleTrack(props),
-}));
-
-const Switch = defineMultiStyleConfig({
- baseStyle,
-});
-
-export default Switch;
diff --git a/theme/components/Table.ts b/theme/components/Table.ts
deleted file mode 100644
index 583cd0ece7..0000000000
--- a/theme/components/Table.ts
+++ /dev/null
@@ -1,104 +0,0 @@
-import { tableAnatomy as parts } from '@chakra-ui/anatomy';
-import {
- createMultiStyleConfigHelpers,
-} from '@chakra-ui/styled-system';
-import { mode } from '@chakra-ui/theme-tools';
-
-import getDefaultTransitionProps from '../utils/getDefaultTransitionProps';
-
-const { defineMultiStyleConfig, definePartsStyle } =
- createMultiStyleConfigHelpers(parts.keys);
-
-const variantSimple = definePartsStyle((props) => {
- const transitionProps = getDefaultTransitionProps();
-
- return {
- th: {
- border: 0,
- color: mode('blackAlpha.700', 'whiteAlpha.700')(props),
- backgroundColor: mode('blackAlpha.100', 'whiteAlpha.200')(props),
- ...transitionProps,
- },
- thead: {
- ...transitionProps,
- },
- td: {
- borderColor: 'divider',
- ...transitionProps,
- },
- };
-});
-
-const sizes = {
- sm: definePartsStyle({
- th: {
- px: '6px',
- py: '10px',
- fontSize: 'sm',
- _first: {
- pl: 3,
- },
- _last: {
- pr: 3,
- },
- },
- td: {
- px: '6px',
- py: 4,
- fontSize: 'sm',
- fontWeight: 500,
- lineHeight: 5,
- _first: {
- pl: 3,
- },
- _last: {
- pr: 3,
- },
- },
- }),
-};
-
-const variants = {
- simple: variantSimple,
-};
-
-const baseStyle = definePartsStyle({
- th: {
- textTransform: 'none',
- fontFamily: 'body',
- fontWeight: '500',
- overflow: 'hidden',
- color: 'gray.500',
- letterSpacing: 'none',
- _first: {
- borderTopLeftRadius: '8px',
- },
- _last: {
- borderTopRightRadius: '8px',
- },
- },
- td: {
- fontSize: 'md',
- verticalAlign: 'top',
- },
- table: {
- tableLayout: 'fixed',
- borderTopLeftRadius: 'base',
- borderTopRightRadius: 'base',
- overflow: 'unset',
- fontVariant: 'normal',
- fontVariantLigatures: 'no-contextual',
- },
-});
-
-const Table = defineMultiStyleConfig({
- baseStyle,
- sizes,
- variants,
- defaultProps: {
- size: 'sm',
- variant: 'simple',
- },
-});
-
-export default Table;
diff --git a/theme/components/Tabs.ts b/theme/components/Tabs.ts
deleted file mode 100644
index f64bec2886..0000000000
--- a/theme/components/Tabs.ts
+++ /dev/null
@@ -1,88 +0,0 @@
-import { tabsAnatomy as parts } from '@chakra-ui/anatomy';
-import {
- createMultiStyleConfigHelpers,
-} from '@chakra-ui/styled-system';
-import { mode } from '@chakra-ui/theme-tools';
-const { defineMultiStyleConfig, definePartsStyle } =
- createMultiStyleConfigHelpers(parts.keys);
-
-import Button from './Button/Button';
-
-const variantSoftRounded = definePartsStyle((props) => {
- return {
- tab: {
- borderRadius: 'base',
- fontWeight: '600',
- color: mode('blue.700', 'gray.400')(props),
- _selected: {
- color: mode('blue.700', 'gray.50')(props),
- bg: mode('blue.50', 'gray.800')(props),
- _hover: {
- color: mode('blue.700', 'gray.50')(props),
- },
- },
- _hover: {
- color: 'link_hovered',
- },
- _focusVisible: {
- boxShadow: { base: 'none', lg: 'outline' },
- },
- },
- };
-});
-
-const variantOutline = definePartsStyle((props) => {
- return {
- tab: {
- ...Button.variants?.outline(props),
- ...Button.baseStyle,
- _selected: Button.variants?.outline(props)._active,
- },
- };
-});
-
-const variantRadioGroup = definePartsStyle((props) => {
- return {
- tab: {
- ...Button.baseStyle,
- ...Button.variants?.radio_group(props),
- _selected: Button.variants?.radio_group(props)?.[`
- &[data-selected=true],
- &[data-selected=true][aria-selected=true]
- `],
- borderRadius: 'none',
- '&[role="tab"]': {
- _first: {
- borderTopLeftRadius: 'base',
- borderBottomLeftRadius: 'base',
- },
- _last: {
- borderTopRightRadius: 'base',
- borderBottomRightRadius: 'base',
- },
- },
- },
- };
-});
-
-const sizes = {
- sm: definePartsStyle({
- tab: Button.sizes?.sm,
- }),
- md: definePartsStyle({
- tab: Button.sizes?.md,
- }),
-};
-
-const variants = {
- 'soft-rounded': variantSoftRounded,
- outline: variantOutline,
- radio_group: variantRadioGroup,
-};
-
-const Tabs = defineMultiStyleConfig({
- sizes,
- variants,
-});
-
-export default Tabs;
diff --git a/theme/components/Tag/Tag.pw.tsx b/theme/components/Tag/Tag.pw.tsx
deleted file mode 100644
index 28da22a9ef..0000000000
--- a/theme/components/Tag/Tag.pw.tsx
+++ /dev/null
@@ -1,20 +0,0 @@
-import { Box, Tag } from '@chakra-ui/react';
-import React from 'react';
-
-import { test, expect } from 'playwright/lib';
-
-[ 'blue', 'gray', 'gray-blue', 'orange', 'green', 'purple', 'cyan', 'teal' ].forEach((colorScheme) => {
- test(`${ colorScheme } color scheme +@dark-mode`, async({ render }) => {
- const component = await render(content );
- await expect(component.getByText(/content/i)).toHaveScreenshot();
- });
-});
-
-test('with long text', async({ render }) => {
- const component = await render(
-
- this is very looooooooooong text
- ,
- );
- await expect(component.getByText(/this/i)).toHaveScreenshot();
-});
diff --git a/theme/components/Tag/Tag.ts b/theme/components/Tag/Tag.ts
deleted file mode 100644
index 240a2a1c26..0000000000
--- a/theme/components/Tag/Tag.ts
+++ /dev/null
@@ -1,87 +0,0 @@
-import { tagAnatomy as parts } from '@chakra-ui/anatomy';
-import {
- createMultiStyleConfigHelpers,
- defineStyle,
-} from '@chakra-ui/styled-system';
-import { mode } from '@chakra-ui/theme-tools';
-
-import getDefaultTransitionProps from '../../utils/getDefaultTransitionProps';
-import Badge from '../Badge';
-
-const transitionProps = getDefaultTransitionProps();
-
-const { defineMultiStyleConfig, definePartsStyle } =
- createMultiStyleConfigHelpers(parts.keys);
-
-const variants = {
- subtle: definePartsStyle((props) => ({
- container: Badge.variants?.subtle(props),
- })),
- select: definePartsStyle((props) => ({
- container: {
- bg: mode('gray.100', 'gray.800')(props),
- color: mode('gray.500', 'whiteAlpha.800')(props),
- cursor: 'pointer',
- _hover: {
- color: 'blue.400',
- opacity: 0.76,
- },
- [`
- &[data-selected=true],
- &[data-selected=true][aria-selected=true]
- `]: {
- bg: mode('blue.500', 'blue.900')(props),
- color: 'whiteAlpha.800',
- },
- },
- })),
-};
-
-const sizes = {
- sm: definePartsStyle({
- container: {
- minH: 6,
- minW: 6,
- fontSize: 'sm',
- px: 1,
- py: '2px',
- lineHeight: 5,
- },
- }),
- md: definePartsStyle({
- container: {
- minH: 8,
- minW: 8,
- fontSize: 'sm',
- px: '6px',
- py: '6px',
- lineHeight: 5,
- },
- }),
-};
-
-const baseStyleContainer = defineStyle({
- display: 'inline-block',
- overflow: 'hidden',
- textOverflow: 'ellipsis',
- whiteSpace: 'nowrap',
- borderRadius: 'sm',
- ...transitionProps,
-});
-
-const baseStyle = definePartsStyle({
- container: baseStyleContainer,
-});
-
-const Tag = defineMultiStyleConfig({
- baseStyle,
- variants,
- sizes,
- defaultProps: {
- size: 'sm',
- variant: 'subtle',
- colorScheme: 'gray',
- },
-});
-
-export default Tag;
diff --git a/theme/components/Tag/__screenshots__/Tag.pw.tsx_dark-color-mode_blue-color-scheme-dark-mode-1.png b/theme/components/Tag/__screenshots__/Tag.pw.tsx_dark-color-mode_blue-color-scheme-dark-mode-1.png
deleted file mode 100644
index cc5ed7c640..0000000000
Binary files a/theme/components/Tag/__screenshots__/Tag.pw.tsx_dark-color-mode_blue-color-scheme-dark-mode-1.png and /dev/null differ
diff --git a/theme/components/Tag/__screenshots__/Tag.pw.tsx_dark-color-mode_cyan-color-scheme-dark-mode-1.png b/theme/components/Tag/__screenshots__/Tag.pw.tsx_dark-color-mode_cyan-color-scheme-dark-mode-1.png
deleted file mode 100644
index 93e36ae37d..0000000000
Binary files a/theme/components/Tag/__screenshots__/Tag.pw.tsx_dark-color-mode_cyan-color-scheme-dark-mode-1.png and /dev/null differ
diff --git a/theme/components/Tag/__screenshots__/Tag.pw.tsx_dark-color-mode_gray-blue-color-scheme-dark-mode-1.png b/theme/components/Tag/__screenshots__/Tag.pw.tsx_dark-color-mode_gray-blue-color-scheme-dark-mode-1.png
deleted file mode 100644
index 991537c134..0000000000
Binary files a/theme/components/Tag/__screenshots__/Tag.pw.tsx_dark-color-mode_gray-blue-color-scheme-dark-mode-1.png and /dev/null differ
diff --git a/theme/components/Tag/__screenshots__/Tag.pw.tsx_dark-color-mode_gray-color-scheme-dark-mode-1.png b/theme/components/Tag/__screenshots__/Tag.pw.tsx_dark-color-mode_gray-color-scheme-dark-mode-1.png
deleted file mode 100644
index 2cc37e85b8..0000000000
Binary files a/theme/components/Tag/__screenshots__/Tag.pw.tsx_dark-color-mode_gray-color-scheme-dark-mode-1.png and /dev/null differ
diff --git a/theme/components/Tag/__screenshots__/Tag.pw.tsx_dark-color-mode_green-color-scheme-dark-mode-1.png b/theme/components/Tag/__screenshots__/Tag.pw.tsx_dark-color-mode_green-color-scheme-dark-mode-1.png
deleted file mode 100644
index acd4ad7582..0000000000
Binary files a/theme/components/Tag/__screenshots__/Tag.pw.tsx_dark-color-mode_green-color-scheme-dark-mode-1.png and /dev/null differ
diff --git a/theme/components/Tag/__screenshots__/Tag.pw.tsx_dark-color-mode_orange-color-scheme-dark-mode-1.png b/theme/components/Tag/__screenshots__/Tag.pw.tsx_dark-color-mode_orange-color-scheme-dark-mode-1.png
deleted file mode 100644
index 0079acf091..0000000000
Binary files a/theme/components/Tag/__screenshots__/Tag.pw.tsx_dark-color-mode_orange-color-scheme-dark-mode-1.png and /dev/null differ
diff --git a/theme/components/Tag/__screenshots__/Tag.pw.tsx_dark-color-mode_purple-color-scheme-dark-mode-1.png b/theme/components/Tag/__screenshots__/Tag.pw.tsx_dark-color-mode_purple-color-scheme-dark-mode-1.png
deleted file mode 100644
index 7d5714466f..0000000000
Binary files a/theme/components/Tag/__screenshots__/Tag.pw.tsx_dark-color-mode_purple-color-scheme-dark-mode-1.png and /dev/null differ
diff --git a/theme/components/Tag/__screenshots__/Tag.pw.tsx_dark-color-mode_teal-color-scheme-dark-mode-1.png b/theme/components/Tag/__screenshots__/Tag.pw.tsx_dark-color-mode_teal-color-scheme-dark-mode-1.png
deleted file mode 100644
index a5c299c701..0000000000
Binary files a/theme/components/Tag/__screenshots__/Tag.pw.tsx_dark-color-mode_teal-color-scheme-dark-mode-1.png and /dev/null differ
diff --git a/theme/components/Tag/__screenshots__/Tag.pw.tsx_default_blue-color-scheme-dark-mode-1.png b/theme/components/Tag/__screenshots__/Tag.pw.tsx_default_blue-color-scheme-dark-mode-1.png
deleted file mode 100644
index ce7946c035..0000000000
Binary files a/theme/components/Tag/__screenshots__/Tag.pw.tsx_default_blue-color-scheme-dark-mode-1.png and /dev/null differ
diff --git a/theme/components/Tag/__screenshots__/Tag.pw.tsx_default_cyan-color-scheme-dark-mode-1.png b/theme/components/Tag/__screenshots__/Tag.pw.tsx_default_cyan-color-scheme-dark-mode-1.png
deleted file mode 100644
index 1cac3c3c0c..0000000000
Binary files a/theme/components/Tag/__screenshots__/Tag.pw.tsx_default_cyan-color-scheme-dark-mode-1.png and /dev/null differ
diff --git a/theme/components/Tag/__screenshots__/Tag.pw.tsx_default_gray-blue-color-scheme-dark-mode-1.png b/theme/components/Tag/__screenshots__/Tag.pw.tsx_default_gray-blue-color-scheme-dark-mode-1.png
deleted file mode 100644
index b3f72fbba9..0000000000
Binary files a/theme/components/Tag/__screenshots__/Tag.pw.tsx_default_gray-blue-color-scheme-dark-mode-1.png and /dev/null differ
diff --git a/theme/components/Tag/__screenshots__/Tag.pw.tsx_default_gray-color-scheme-dark-mode-1.png b/theme/components/Tag/__screenshots__/Tag.pw.tsx_default_gray-color-scheme-dark-mode-1.png
deleted file mode 100644
index fadfdd7262..0000000000
Binary files a/theme/components/Tag/__screenshots__/Tag.pw.tsx_default_gray-color-scheme-dark-mode-1.png and /dev/null differ
diff --git a/theme/components/Tag/__screenshots__/Tag.pw.tsx_default_green-color-scheme-dark-mode-1.png b/theme/components/Tag/__screenshots__/Tag.pw.tsx_default_green-color-scheme-dark-mode-1.png
deleted file mode 100644
index 926ff7e591..0000000000
Binary files a/theme/components/Tag/__screenshots__/Tag.pw.tsx_default_green-color-scheme-dark-mode-1.png and /dev/null differ
diff --git a/theme/components/Tag/__screenshots__/Tag.pw.tsx_default_orange-color-scheme-dark-mode-1.png b/theme/components/Tag/__screenshots__/Tag.pw.tsx_default_orange-color-scheme-dark-mode-1.png
deleted file mode 100644
index 3b8b6c4939..0000000000
Binary files a/theme/components/Tag/__screenshots__/Tag.pw.tsx_default_orange-color-scheme-dark-mode-1.png and /dev/null differ
diff --git a/theme/components/Tag/__screenshots__/Tag.pw.tsx_default_purple-color-scheme-dark-mode-1.png b/theme/components/Tag/__screenshots__/Tag.pw.tsx_default_purple-color-scheme-dark-mode-1.png
deleted file mode 100644
index aec0669393..0000000000
Binary files a/theme/components/Tag/__screenshots__/Tag.pw.tsx_default_purple-color-scheme-dark-mode-1.png and /dev/null differ
diff --git a/theme/components/Tag/__screenshots__/Tag.pw.tsx_default_teal-color-scheme-dark-mode-1.png b/theme/components/Tag/__screenshots__/Tag.pw.tsx_default_teal-color-scheme-dark-mode-1.png
deleted file mode 100644
index 8b713c96bc..0000000000
Binary files a/theme/components/Tag/__screenshots__/Tag.pw.tsx_default_teal-color-scheme-dark-mode-1.png and /dev/null differ
diff --git a/theme/components/Tag/__screenshots__/Tag.pw.tsx_default_with-long-text-1.png b/theme/components/Tag/__screenshots__/Tag.pw.tsx_default_with-long-text-1.png
deleted file mode 100644
index 7ce1510b5b..0000000000
Binary files a/theme/components/Tag/__screenshots__/Tag.pw.tsx_default_with-long-text-1.png and /dev/null differ
diff --git a/theme/components/Text.ts b/theme/components/Text.ts
deleted file mode 100644
index 435330bc3b..0000000000
--- a/theme/components/Text.ts
+++ /dev/null
@@ -1,32 +0,0 @@
-import type { SystemStyleInterpolation } from '@chakra-ui/styled-system';
-import { defineStyle, defineStyleConfig } from '@chakra-ui/styled-system';
-import { mode } from '@chakra-ui/theme-tools';
-
-const variantPrimary = defineStyle((props) => ({
- color: mode('blackAlpha.800', 'whiteAlpha.800')(props),
-}));
-
-const variantSecondary = defineStyle((props) => ({
- color: mode('gray.500', 'gray.400')(props),
-}));
-
-const variantInherit = {
- color: 'inherit',
-};
-
-const variants: Record = {
- primary: variantPrimary,
- secondary: variantSecondary,
- inherit: variantInherit,
-};
-
-const defaultProps = {
- variant: 'primary',
-};
-
-const Text = defineStyleConfig({
- defaultProps,
- variants,
-});
-
-export default Text;
diff --git a/theme/components/Textarea.ts b/theme/components/Textarea.ts
deleted file mode 100644
index bb0a00ca56..0000000000
--- a/theme/components/Textarea.ts
+++ /dev/null
@@ -1,38 +0,0 @@
-import { Textarea as TextareaComponent } from '@chakra-ui/react';
-import { defineStyle, defineStyleConfig } from '@chakra-ui/styled-system';
-
-import getOutlinedFieldStyles from '../utils/getOutlinedFieldStyles';
-
-const sizes = {
- md: defineStyle({
- fontSize: 'md',
- lineHeight: '20px',
- h: '160px',
- borderRadius: 'base',
- }),
- lg: defineStyle({
- fontSize: 'md',
- lineHeight: '20px',
- px: '24px',
- py: '28px',
- h: '160px',
- borderRadius: 'base',
- }),
-};
-
-const Textarea = defineStyleConfig({
- sizes,
- variants: {
- outline: defineStyle(getOutlinedFieldStyles),
- },
- defaultProps: {
- variant: 'outline',
- },
-});
-
-TextareaComponent.defaultProps = {
- ...TextareaComponent.defaultProps,
- placeholder: ' ',
-};
-
-export default Textarea;
diff --git a/theme/components/Tooltip/Tooltip.pw.tsx b/theme/components/Tooltip/Tooltip.pw.tsx
deleted file mode 100644
index e41255ca61..0000000000
--- a/theme/components/Tooltip/Tooltip.pw.tsx
+++ /dev/null
@@ -1,38 +0,0 @@
-import { Box, Tooltip, Icon } from '@chakra-ui/react';
-import React from 'react';
-
-import { test, expect } from 'playwright/lib';
-
-test('base view +@dark-mode', async({ render, page }) => {
- const component = await render(
-
-
- trigger
-
- ,
- );
-
- await component.getByText(/trigger/i).hover();
-
- await expect(page).toHaveScreenshot({ clip: { x: 0, y: 40, width: 130, height: 64 } });
-});
-
-// was not able to reproduce in tests issue when Icon is used as trigger for tooltip
-// https://github.com/chakra-ui/chakra-ui/issues/7107
-test.fixme('with icon', async({ render, page }) => {
- const component = await render(
-
-
-
-
-
-
- ,
- );
-
- const tooltip = page.getByText(/tooltip content/i);
- await expect(tooltip).toBeHidden();
-
- await component.locator('svg[aria-label="Trigger"]').hover();
- await expect(tooltip).toBeVisible();
-});
diff --git a/theme/components/Tooltip/Tooltip.ts b/theme/components/Tooltip/Tooltip.ts
deleted file mode 100644
index d0bbae57e2..0000000000
--- a/theme/components/Tooltip/Tooltip.ts
+++ /dev/null
@@ -1,50 +0,0 @@
-import { Tooltip as TooltipComponent } from '@chakra-ui/react';
-import { defineStyle, defineStyleConfig } from '@chakra-ui/styled-system';
-import { mode, cssVar } from '@chakra-ui/theme-tools';
-
-const $bg = cssVar('tooltip-bg');
-const $fg = cssVar('tooltip-fg');
-const $arrowBg = cssVar('popper-arrow-bg');
-
-const variantNav = defineStyle((props) => {
- return {
- bg: mode('blue.50', 'gray.800')(props),
- color: 'blue.400',
- padding: '15px 12px',
- minWidth: '120px',
- borderRadius: 'base',
- fontSize: '14px',
- lineHeight: '20px',
- textAlign: 'center',
- boxShadow: 'none',
- fontWeight: '500',
- };
-});
-
-const variants = {
- nav: variantNav,
-};
-
-const baseStyle = defineStyle((props) => {
- const bg = mode('gray.700', 'gray.200')(props);
- const fg = mode('white', 'black')(props);
-
- return {
- bg: $bg.reference,
- color: $fg.reference,
- [$bg.variable]: `colors.${ bg }`,
- [$fg.variable]: `colors.${ fg }`,
- [$arrowBg.variable]: $bg.reference,
- maxWidth: props.maxWidth || props.maxW || 'calc(100vw - 8px)',
- marginX: '4px',
- };
-});
-
-const Tooltip = defineStyleConfig({
- variants,
- baseStyle,
-});
-
-TooltipComponent.defaultProps = { ...TooltipComponent.defaultProps, hasArrow: true };
-
-export default Tooltip;
diff --git a/theme/components/Tooltip/__screenshots__/Tooltip.pw.tsx_dark-color-mode_base-view-dark-mode-1.png b/theme/components/Tooltip/__screenshots__/Tooltip.pw.tsx_dark-color-mode_base-view-dark-mode-1.png
deleted file mode 100644
index 8b61705f88..0000000000
Binary files a/theme/components/Tooltip/__screenshots__/Tooltip.pw.tsx_dark-color-mode_base-view-dark-mode-1.png and /dev/null differ
diff --git a/theme/components/Tooltip/__screenshots__/Tooltip.pw.tsx_default_base-view-dark-mode-1.png b/theme/components/Tooltip/__screenshots__/Tooltip.pw.tsx_default_base-view-dark-mode-1.png
deleted file mode 100644
index ab9a1ac7b9..0000000000
Binary files a/theme/components/Tooltip/__screenshots__/Tooltip.pw.tsx_default_base-view-dark-mode-1.png and /dev/null differ
diff --git a/theme/components/index.ts b/theme/components/index.ts
deleted file mode 100644
index 61903f55f6..0000000000
--- a/theme/components/index.ts
+++ /dev/null
@@ -1,55 +0,0 @@
-import Alert from './Alert/Alert';
-import Badge from './Badge';
-import Button from './Button/Button';
-import Checkbox from './Checkbox';
-import Drawer from './Drawer';
-import Form from './Form';
-import FormLabel from './FormLabel';
-import Heading from './Heading';
-import Input from './Input';
-import Link from './Link';
-import Menu from './Menu';
-import Modal from './Modal';
-import PinInput from './PinInput';
-import Popover from './Popover';
-import Radio from './Radio';
-import Select from './Select';
-import Skeleton from './Skeleton';
-import Spinner from './Spinner';
-import Switch from './Switch';
-import Table from './Table';
-import Tabs from './Tabs';
-import Tag from './Tag/Tag';
-import Text from './Text';
-import Textarea from './Textarea';
-import Tooltip from './Tooltip/Tooltip';
-
-const components = {
- Alert,
- Badge,
- Button,
- Checkbox,
- Drawer,
- Heading,
- Input,
- Form,
- FormLabel,
- Link,
- Menu,
- Modal,
- PinInput,
- Popover,
- Radio,
- Select,
- Skeleton,
- Spinner,
- Switch,
- Tabs,
- Table,
- Tag,
- Text,
- Textarea,
- Tooltip,
-};
-
-export default components;
diff --git a/theme/config.ts b/theme/config.ts
deleted file mode 100644
index ddca2b54ea..0000000000
--- a/theme/config.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-import { type ThemeConfig } from '@chakra-ui/react';
-
-import appConfig from 'configs/app';
-
-const config: ThemeConfig = {
- initialColorMode: appConfig.UI.colorTheme.default?.colorMode ?? 'system',
- useSystemColorMode: false,
- disableTransitionOnChange: false,
-};
-
-export default config;
diff --git a/theme/foundations/borders.ts b/theme/foundations/borders.ts
deleted file mode 100644
index 4307c548c1..0000000000
--- a/theme/foundations/borders.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-const borders = {
- radii: {
- none: '0',
- sm: '4px',
- base: '8px',
- md: '12px',
- lg: '16px',
- xl: '24px',
- full: '9999px',
- },
-};
-
-export default borders;
diff --git a/theme/foundations/colors.ts b/theme/foundations/colors.ts
deleted file mode 100644
index d1e3dae714..0000000000
--- a/theme/foundations/colors.ts
+++ /dev/null
@@ -1,66 +0,0 @@
-const colors = {
- green: {
- '100': '#C6F6D5',
- '400': '#48BB78',
- '500': '#38A169',
- '600': '#25855A',
- },
- blue: {
- '50': '#EBF8FF',
- '100': '#BEE3F8',
- '200': '#90CDF4',
- '300': '#63B3ED',
- '400': '#4299E1',
- '500': '#3182CE',
- '600': '#2B6CB0',
- '700': '#2C5282',
- '800': '#2A4365',
- '900': '#1A365D',
- },
- red: {
- '500': '#E53E3E',
- '100': '#FED7D7',
- },
- orange: {
- '100': '#FEEBCB',
- },
- gray: {
- '50': '#F7FAFC', // <-
- '100': '#EDF2F7',
- '200': '#E2E8F0',
- '300': '#CBD5E0',
- '400': '#A0AEC0',
- '500': '#718096',
- '600': '#4A5568',
- '700': '#2D3748',
- '800': '#1A202C',
- '900': '#171923',
- },
- black: '#101112',
- white: '#ffffff',
- blackAlpha: {
- '50': 'RGBA(16, 17, 18, 0.04)',
- '100': 'RGBA(16, 17, 18, 0.06)',
- '200': 'RGBA(16, 17, 18, 0.08)',
- '300': 'RGBA(16, 17, 18, 0.16)',
- '400': 'RGBA(16, 17, 18, 0.24)',
- '500': 'RGBA(16, 17, 18, 0.36)',
- '600': 'RGBA(16, 17, 18, 0.48)',
- '700': 'RGBA(16, 17, 18, 0.64)',
- '800': 'RGBA(16, 17, 18, 0.80)',
- '900': 'RGBA(16, 17, 18, 0.92)',
- },
- github: '#171923',
- telegram: '#2775CA',
- linkedin: '#1564BA',
- discord: '#9747FF',
- slack: '#1BA27A',
- twitter: '#000000',
- opensea: '#2081E2',
- facebook: '#4460A0',
- medium: '#231F20',
- reddit: '#FF4500',
- celo: '#FCFF52',
-};
-
-export default colors;
diff --git a/theme/foundations/scrollbar.ts b/theme/foundations/scrollbar.ts
deleted file mode 100644
index 3d9d68bf11..0000000000
--- a/theme/foundations/scrollbar.ts
+++ /dev/null
@@ -1,40 +0,0 @@
-import { getCSSVar } from '@chakra-ui/styled-system';
-import { mode } from '@chakra-ui/theme-tools';
-import type { StyleFunctionProps } from '@chakra-ui/theme-tools';
-
-const scrollbar = (props: StyleFunctionProps) => {
- const bgColor = mode('blackAlpha.300', 'whiteAlpha.300')(props);
- const resizerUrl = mode('url(/static/resizer_light.png)', 'url(/static/resizer_dark.png)')(props);
-
- return {
- 'body *::-webkit-scrollbar': {
- width: '20px',
- },
- 'body *::-webkit-scrollbar-track': {
- backgroundColor: 'transparent',
- },
- 'body *::-webkit-scrollbar-thumb': {
- backgroundColor: bgColor,
- borderRadius: '20px',
- border: `8px solid rgba(0,0,0,0)`,
- backgroundClip: 'content-box',
- minHeight: '32px',
- },
- 'body *::-webkit-scrollbar-button': {
- display: 'none',
- },
- 'body *::-webkit-scrollbar-corner': {
- backgroundColor: 'transparent',
- },
- 'body *::-webkit-resizer': {
- backgroundImage: resizerUrl,
- backgroundSize: '20px',
- },
- 'body *': {
- scrollbarWidth: 'thin',
- scrollbarColor: `${ getCSSVar(props.theme, 'colors', bgColor) } transparent`,
- },
- };
-};
-
-export default scrollbar;
diff --git a/theme/foundations/semanticTokens.ts b/theme/foundations/semanticTokens.ts
deleted file mode 100644
index 0c772a6539..0000000000
--- a/theme/foundations/semanticTokens.ts
+++ /dev/null
@@ -1,44 +0,0 @@
-const semanticTokens = {
- colors: {
- divider: {
- 'default': 'blackAlpha.200',
- _dark: 'whiteAlpha.200',
- },
- text: {
- 'default': 'blackAlpha.800',
- _dark: 'whiteAlpha.800',
- },
- text_secondary: {
- 'default': 'gray.500',
- _dark: 'gray.400',
- },
- link: {
- 'default': 'blue.600',
- _dark: 'blue.300',
- },
- link_hovered: {
- 'default': 'blue.400',
- },
- icon_link_external: {
- 'default': 'gray.300',
- _dark: 'gray.500',
- },
- icon_info: {
- 'default': 'gray.400',
- _dark: 'gray.500',
- },
- error: {
- 'default': 'red.500',
- _dark: 'red.500',
- },
- dialog_bg: {
- 'default': 'white',
- _dark: 'gray.900',
- },
- },
- shadows: {
- action_bar: '0 4px 4px -4px rgb(0 0 0 / 10%), 0 2px 4px -4px rgb(0 0 0 / 6%)',
- },
-};
-
-export default semanticTokens;
diff --git a/theme/foundations/transition.ts b/theme/foundations/transition.ts
deleted file mode 100644
index b120e48137..0000000000
--- a/theme/foundations/transition.ts
+++ /dev/null
@@ -1,15 +0,0 @@
-const transitionDuration = {
- 'ultra-fast': '50ms',
- faster: '100ms',
- fast: '150ms',
- normal: '200ms',
- slow: '300ms',
- slower: '400ms',
- 'ultra-slow': '500ms',
-};
-
-const transition = {
- duration: transitionDuration,
-};
-
-export default transition;
diff --git a/theme/foundations/typography.ts b/theme/foundations/typography.ts
deleted file mode 100644
index b129403a93..0000000000
--- a/theme/foundations/typography.ts
+++ /dev/null
@@ -1,35 +0,0 @@
-import { theme } from '@chakra-ui/react';
-
-import config from 'configs/app';
-
-export const BODY_TYPEFACE = config.UI.fonts.body?.name ?? 'Inter';
-export const HEADING_TYPEFACE = config.UI.fonts.heading?.name ?? 'Poppins';
-
-const typography = {
- fonts: {
- heading: `${ HEADING_TYPEFACE }, ${ theme.fonts.heading }`,
- body: `${ BODY_TYPEFACE }, ${ theme.fonts.body }`,
- },
- textStyles: {
- h2: {
- fontSize: [ '32px' ],
- fontWeight: '500',
- lineHeight: '40px',
- fontFamily: 'heading',
- },
- h3: {
- fontSize: '24px',
- fontWeight: '500',
- lineHeight: '32px',
- fontFamily: 'heading',
- },
- h4: {
- fontSize: 'md',
- fontWeight: '500',
- lineHeight: '24px',
- fontFamily: 'heading',
- },
- },
-};
-
-export default typography;
diff --git a/theme/foundations/zIndices.ts b/theme/foundations/zIndices.ts
deleted file mode 100644
index 84d6f039f8..0000000000
--- a/theme/foundations/zIndices.ts
+++ /dev/null
@@ -1,19 +0,0 @@
-const zIndices = {
- hide: -1,
- auto: 'auto',
- base: 0,
- docked: 10,
- dropdown: 1000,
- sticky: 1100,
- sticky1: 1101,
- sticky2: 1102,
- banner: 1200,
- overlay: 1300,
- modal: 1400,
- popover: 1500,
- tooltip: 1550, // otherwise tooltips will not be visible in modals
- skipLink: 1600,
- toast: 1700,
-};
-
-export default zIndices;
diff --git a/theme/global.ts b/theme/global.ts
deleted file mode 100644
index acdbc2fecb..0000000000
--- a/theme/global.ts
+++ /dev/null
@@ -1,32 +0,0 @@
-import type { StyleFunctionProps } from '@chakra-ui/theme-tools';
-import { mode } from '@chakra-ui/theme-tools';
-
-import scrollbar from './foundations/scrollbar';
-import addressEntity from './globals/address-entity';
-import recaptcha from './globals/recaptcha';
-import getDefaultTransitionProps from './utils/getDefaultTransitionProps';
-
-const global = (props: StyleFunctionProps) => ({
- body: {
- bg: mode('white', 'black')(props),
- ...getDefaultTransitionProps(),
- '-webkit-tap-highlight-color': 'transparent',
- 'font-variant-ligatures': 'no-contextual',
- },
- mark: {
- bgColor: mode('green.100', 'green.800')(props),
- color: 'inherit',
- },
- 'svg *::selection': {
- color: 'none',
- background: 'none',
- },
- form: {
- w: '100%',
- },
- ...scrollbar(props),
- ...addressEntity(props),
- ...recaptcha(),
-});
-
-export default global;
diff --git a/theme/globals/address-entity.ts b/theme/globals/address-entity.ts
deleted file mode 100644
index 25641c3020..0000000000
--- a/theme/globals/address-entity.ts
+++ /dev/null
@@ -1,37 +0,0 @@
-import { mode } from '@chakra-ui/theme-tools';
-import type { StyleFunctionProps } from '@chakra-ui/theme-tools';
-
-const styles = (props: StyleFunctionProps) => {
- return {
- '.address-entity': {
- '&.address-entity_highlighted': {
- _before: {
- content: `" "`,
- position: 'absolute',
- py: 1,
- pl: 1,
- pr: 0,
- top: '-5px',
- left: '-5px',
- width: `100%`,
- height: '100%',
- borderRadius: 'base',
- borderColor: mode('blue.200', 'blue.600')(props),
- borderWidth: '1px',
- borderStyle: 'dashed',
- bgColor: mode('blue.50', 'blue.900')(props),
- zIndex: -1,
- },
- },
- },
- '.address-entity_no-copy': {
- '&.address-entity_highlighted': {
- _before: {
- pr: 2,
- },
- },
- },
- };
-};
-
-export default styles;
diff --git a/theme/globals/recaptcha.ts b/theme/globals/recaptcha.ts
deleted file mode 100644
index c551eb4ae2..0000000000
--- a/theme/globals/recaptcha.ts
+++ /dev/null
@@ -1,22 +0,0 @@
-const styles = () => {
- return {
- '.grecaptcha-badge': {
- visibility: 'hidden',
- },
- 'div:has(div):has(iframe[title="recaptcha challenge expires in two minutes"])': {
- '&::after': {
- content: `" "`,
- display: 'block',
- position: 'fixed',
- top: 0,
- left: 0,
- width: '100vw',
- height: '100vh',
- zIndex: 100000,
- bgColor: 'blackAlpha.300',
- },
- },
- };
-};
-
-export default styles;
diff --git a/theme/package.json b/theme/package.json
deleted file mode 100644
index 9f4fe3a558..0000000000
--- a/theme/package.json
+++ /dev/null
@@ -1,24 +0,0 @@
-{
- "name": "@blockscout/chakra-theme",
- "version": "1.32.0",
- "main": "./dist/index.js",
- "license": "MIT",
- "repository": {
- "type": "git",
- "url": "https://github.com/blockscout/frontend.git",
- "directory": "theme"
- },
- "scripts": {
- "build": "yarn webpack-cli -c ./webpack.config.js"
- },
- "devDependencies": {
- "typescript": "5.4.2",
- "ts-loader": "^9.4.4",
- "webpack": "^5.94.0",
- "webpack-cli": "^5.1.4",
- "tsconfig-paths-webpack-plugin": "^4.1.0"
- },
- "files": [
- "/dist"
- ]
-}
diff --git a/theme/theme.ts b/theme/theme.ts
deleted file mode 100644
index 7d391fe6d6..0000000000
--- a/theme/theme.ts
+++ /dev/null
@@ -1,29 +0,0 @@
-import { extendTheme } from '@chakra-ui/react';
-
-import components from './components/index';
-import config from './config';
-import borders from './foundations/borders';
-import breakpoints from './foundations/breakpoints';
-import colors from './foundations/colors';
-import semanticTokens from './foundations/semanticTokens';
-import transition from './foundations/transition';
-import typography from './foundations/typography';
-import zIndices from './foundations/zIndices';
-import global from './global';
-
-const overrides = {
- ...typography,
- ...borders,
- colors,
- components,
- config,
- styles: {
- global,
- },
- breakpoints,
- transition,
- zIndices,
- semanticTokens,
-};
-
-export default extendTheme(overrides);
diff --git a/theme/tsconfig.json b/theme/tsconfig.json
deleted file mode 100644
index 153821f077..0000000000
--- a/theme/tsconfig.json
+++ /dev/null
@@ -1,24 +0,0 @@
-{
- "extends": "../tsconfig.json",
- "compilerOptions": {
- "noEmit": false,
- "target": "es2016",
- "module": "CommonJS",
- "moduleResolution": "node",
- "paths": {
- "nextjs-routes": ["./nextjs/nextjs-routes.d.ts"],
- }
- },
- "include": [
- "../types/**/*.ts",
- "../configs/app/**/*.ts",
- "../global.d.ts",
- "./theme.ts",
- "./components/**/*.pw.tsx",
- ],
- "tsc-alias": {
- "verbose": true,
- "resolveFullPaths": true,
- }
-}
-
\ No newline at end of file
diff --git a/theme/utils/getDefaultTransitionProps.ts b/theme/utils/getDefaultTransitionProps.ts
deleted file mode 100644
index 2f7d60e7a7..0000000000
--- a/theme/utils/getDefaultTransitionProps.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-export default function getDefaultTransitionProps(props?: { transitionProperty: string }) {
- return {
- transitionProperty: `background-color, color, border-color${ props?.transitionProperty ? ', ' + props.transitionProperty : '' }`,
- transitionDuration: 'normal',
- transitionTimingFunction: 'ease',
- };
-}
diff --git a/theme/utils/getFormStyles.ts b/theme/utils/getFormStyles.ts
deleted file mode 100644
index 5dda16db55..0000000000
--- a/theme/utils/getFormStyles.ts
+++ /dev/null
@@ -1,55 +0,0 @@
-import type { StyleFunctionProps } from '@chakra-ui/theme-tools';
-import { mode, transparentize } from '@chakra-ui/theme-tools';
-
-export default function getFormStyles(props: StyleFunctionProps) {
- return {
- input: {
- empty: {
- // there is no text in the empty input
- // color: ???,
- bgColor: props.bgColor || mode('white', 'black')(props),
- borderColor: mode('gray.100', 'gray.700')(props),
- },
- hover: {
- color: mode('gray.800', 'gray.50')(props),
- bgColor: props.bgColor || mode('white', 'black')(props),
- borderColor: mode('gray.200', 'gray.500')(props),
- },
- focus: {
- color: mode('gray.800', 'gray.50')(props),
- bgColor: props.bgColor || mode('white', 'black')(props),
- borderColor: mode('blue.400', 'blue.400')(props),
- },
- filled: {
- color: mode('gray.800', 'gray.50')(props),
- bgColor: props.bgColor || mode('white', 'black')(props),
- borderColor: mode('gray.300', 'gray.600')(props),
- },
- readOnly: {
- color: mode('gray.800', 'gray.50')(props),
- bgColor: mode('gray.200', 'gray.800')(props),
- borderColor: mode('gray.200', 'gray.800')(props),
- },
- // we use opacity to show the disabled state
- disabled: {
- opacity: 0.2,
- },
- error: {
- color: mode('gray.800', 'gray.50')(props),
- bgColor: props.bgColor || mode('white', 'black')(props),
- borderColor: mode('red.500', 'red.500')(props),
- },
- },
- placeholder: {
- 'default': {
- color: mode('gray.500', 'gray.500')(props),
- },
- disabled: {
- color: transparentize('gray.500', 0.2)(props.theme),
- },
- error: {
- color: mode('red.500', 'red.500')(props),
- },
- },
- };
-}
diff --git a/theme/utils/getOutlinedFieldStyles.ts b/theme/utils/getOutlinedFieldStyles.ts
deleted file mode 100644
index c26ed15d11..0000000000
--- a/theme/utils/getOutlinedFieldStyles.ts
+++ /dev/null
@@ -1,68 +0,0 @@
-import type { StyleFunctionProps } from '@chakra-ui/theme-tools';
-import { mode } from '@chakra-ui/theme-tools';
-
-import getDefaultTransitionProps from './getDefaultTransitionProps';
-import getFormStyles from './getFormStyles';
-
-export default function getOutlinedFieldStyles(props: StyleFunctionProps) {
- const formStyles = getFormStyles(props);
- const transitionProps = getDefaultTransitionProps();
-
- return {
- border: '2px solid',
- // filled input
- ...formStyles.input.filled,
- ...transitionProps,
- _hover: {
- ...formStyles.input.hover,
- },
- _readOnly: {
- boxShadow: 'none !important',
- userSelect: 'all',
- pointerEvents: 'none',
- ...formStyles.input.readOnly,
- _hover: {
- ...formStyles.input.readOnly,
- },
- _focus: {
- ...formStyles.input.readOnly,
- },
- },
- _disabled: {
- ...formStyles.input.disabled,
- cursor: 'not-allowed',
- ':-webkit-autofill': {
- // background color for disabled input which value was selected from browser autocomplete popup
- '-webkit-box-shadow': `0 0 0px 1000px ${ mode('rgba(16, 17, 18, 0.08)', 'rgba(255, 255, 255, 0.08)')(props) } inset`,
- },
- },
- _invalid: {
- ...formStyles.input.error,
- boxShadow: `none`,
- _placeholder: {
- color: formStyles.placeholder.error.color,
- },
- },
- _focusVisible: {
- ...formStyles.input.focus,
- zIndex: 1,
- boxShadow: 'md',
- },
- _placeholder: {
- color: formStyles.placeholder.default.color,
- },
- // not filled input
- ':placeholder-shown:not(:focus-visible):not(:hover):not([aria-invalid=true]):not([aria-readonly=true])': {
- ...formStyles.input.empty,
- },
-
- // not filled input with type="date"
- ':not(:placeholder-shown)[value=""]:not(:focus-visible):not(:hover):not([aria-invalid=true]):not([aria-readonly=true])': {
- ...formStyles.input.empty,
- },
-
- ':-webkit-autofill': { transition: 'background-color 5000s ease-in-out 0s' },
- ':-webkit-autofill:hover': { transition: 'background-color 5000s ease-in-out 0s' },
- ':-webkit-autofill:focus': { transition: 'background-color 5000s ease-in-out 0s' },
- };
-}
diff --git a/theme/webpack.config.js b/theme/webpack.config.js
deleted file mode 100644
index 146517ea3e..0000000000
--- a/theme/webpack.config.js
+++ /dev/null
@@ -1,32 +0,0 @@
-const path = require('path');
-const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin');
-
-const config = {
- mode: 'production',
- entry: path.resolve(__dirname) + '/theme.ts',
- module: {
- rules: [
- {
- test: /\.tsx?$/,
- use: 'ts-loader',
- exclude: /node_modules/,
- },
- ],
- },
- resolve: {
- extensions: [ '.tsx', '.ts', '.js' ],
- plugins: [ new TsconfigPathsPlugin({ configFile: './tsconfig.json' }) ],
- },
- output: {
- filename: 'index.js',
- path: path.resolve(__dirname) + '/dist',
- library: {
- type: 'commonjs',
- },
- },
- optimization: {
- minimize: false,
- },
-};
-
-module.exports = config;
diff --git a/theme/yarn.lock b/theme/yarn.lock
deleted file mode 100644
index baa25d2dd7..0000000000
--- a/theme/yarn.lock
+++ /dev/null
@@ -1,918 +0,0 @@
-# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
-# yarn lockfile v1
-
-
-"@discoveryjs/json-ext@^0.5.0":
- version "0.5.7"
- resolved "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz"
- integrity sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==
-
-"@jridgewell/gen-mapping@^0.3.5":
- version "0.3.5"
- resolved "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz"
- integrity sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==
- dependencies:
- "@jridgewell/set-array" "^1.2.1"
- "@jridgewell/sourcemap-codec" "^1.4.10"
- "@jridgewell/trace-mapping" "^0.3.24"
-
-"@jridgewell/resolve-uri@^3.1.0":
- version "3.1.2"
- resolved "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz"
- integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==
-
-"@jridgewell/set-array@^1.2.1":
- version "1.2.1"
- resolved "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz"
- integrity sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==
-
-"@jridgewell/source-map@^0.3.3":
- version "0.3.6"
- resolved "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz"
- integrity sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==
- dependencies:
- "@jridgewell/gen-mapping" "^0.3.5"
- "@jridgewell/trace-mapping" "^0.3.25"
-
-"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14":
- version "1.5.0"
- resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz"
- integrity sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==
-
-"@jridgewell/trace-mapping@^0.3.20", "@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25":
- version "0.3.25"
- resolved "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz"
- integrity sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==
- dependencies:
- "@jridgewell/resolve-uri" "^3.1.0"
- "@jridgewell/sourcemap-codec" "^1.4.14"
-
-"@types/estree@^1.0.5":
- version "1.0.5"
- resolved "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz"
- integrity sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==
-
-"@types/json-schema@^7.0.8":
- version "7.0.15"
- resolved "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz"
- integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==
-
-"@types/node@*":
- version "20.14.11"
- resolved "https://registry.npmjs.org/@types/node/-/node-20.14.11.tgz"
- integrity sha512-kprQpL8MMeszbz6ojB5/tU8PLN4kesnN8Gjzw349rDlNgsSzg90lAVj3llK99Dh7JON+t9AuscPPFW6mPbTnSA==
- dependencies:
- undici-types "~5.26.4"
-
-"@webassemblyjs/ast@1.12.1", "@webassemblyjs/ast@^1.12.1":
- version "1.12.1"
- resolved "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.12.1.tgz"
- integrity sha512-EKfMUOPRRUTy5UII4qJDGPpqfwjOmZ5jeGFwid9mnoqIFK+e0vqoi1qH56JpmZSzEL53jKnNzScdmftJyG5xWg==
- dependencies:
- "@webassemblyjs/helper-numbers" "1.11.6"
- "@webassemblyjs/helper-wasm-bytecode" "1.11.6"
-
-"@webassemblyjs/floating-point-hex-parser@1.11.6":
- version "1.11.6"
- resolved "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz"
- integrity sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==
-
-"@webassemblyjs/helper-api-error@1.11.6":
- version "1.11.6"
- resolved "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz"
- integrity sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==
-
-"@webassemblyjs/helper-buffer@1.12.1":
- version "1.12.1"
- resolved "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.12.1.tgz"
- integrity sha512-nzJwQw99DNDKr9BVCOZcLuJJUlqkJh+kVzVl6Fmq/tI5ZtEyWT1KZMyOXltXLZJmDtvLCDgwsyrkohEtopTXCw==
-
-"@webassemblyjs/helper-numbers@1.11.6":
- version "1.11.6"
- resolved "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz"
- integrity sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==
- dependencies:
- "@webassemblyjs/floating-point-hex-parser" "1.11.6"
- "@webassemblyjs/helper-api-error" "1.11.6"
- "@xtuc/long" "4.2.2"
-
-"@webassemblyjs/helper-wasm-bytecode@1.11.6":
- version "1.11.6"
- resolved "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz"
- integrity sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==
-
-"@webassemblyjs/helper-wasm-section@1.12.1":
- version "1.12.1"
- resolved "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.12.1.tgz"
- integrity sha512-Jif4vfB6FJlUlSbgEMHUyk1j234GTNG9dBJ4XJdOySoj518Xj0oGsNi59cUQF4RRMS9ouBUxDDdyBVfPTypa5g==
- dependencies:
- "@webassemblyjs/ast" "1.12.1"
- "@webassemblyjs/helper-buffer" "1.12.1"
- "@webassemblyjs/helper-wasm-bytecode" "1.11.6"
- "@webassemblyjs/wasm-gen" "1.12.1"
-
-"@webassemblyjs/ieee754@1.11.6":
- version "1.11.6"
- resolved "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz"
- integrity sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==
- dependencies:
- "@xtuc/ieee754" "^1.2.0"
-
-"@webassemblyjs/leb128@1.11.6":
- version "1.11.6"
- resolved "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.6.tgz"
- integrity sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==
- dependencies:
- "@xtuc/long" "4.2.2"
-
-"@webassemblyjs/utf8@1.11.6":
- version "1.11.6"
- resolved "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.6.tgz"
- integrity sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==
-
-"@webassemblyjs/wasm-edit@^1.12.1":
- version "1.12.1"
- resolved "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.12.1.tgz"
- integrity sha512-1DuwbVvADvS5mGnXbE+c9NfA8QRcZ6iKquqjjmR10k6o+zzsRVesil54DKexiowcFCPdr/Q0qaMgB01+SQ1u6g==
- dependencies:
- "@webassemblyjs/ast" "1.12.1"
- "@webassemblyjs/helper-buffer" "1.12.1"
- "@webassemblyjs/helper-wasm-bytecode" "1.11.6"
- "@webassemblyjs/helper-wasm-section" "1.12.1"
- "@webassemblyjs/wasm-gen" "1.12.1"
- "@webassemblyjs/wasm-opt" "1.12.1"
- "@webassemblyjs/wasm-parser" "1.12.1"
- "@webassemblyjs/wast-printer" "1.12.1"
-
-"@webassemblyjs/wasm-gen@1.12.1":
- version "1.12.1"
- resolved "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.12.1.tgz"
- integrity sha512-TDq4Ojh9fcohAw6OIMXqiIcTq5KUXTGRkVxbSo1hQnSy6lAM5GSdfwWeSxpAo0YzgsgF182E/U0mDNhuA0tW7w==
- dependencies:
- "@webassemblyjs/ast" "1.12.1"
- "@webassemblyjs/helper-wasm-bytecode" "1.11.6"
- "@webassemblyjs/ieee754" "1.11.6"
- "@webassemblyjs/leb128" "1.11.6"
- "@webassemblyjs/utf8" "1.11.6"
-
-"@webassemblyjs/wasm-opt@1.12.1":
- version "1.12.1"
- resolved "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.12.1.tgz"
- integrity sha512-Jg99j/2gG2iaz3hijw857AVYekZe2SAskcqlWIZXjji5WStnOpVoat3gQfT/Q5tb2djnCjBtMocY/Su1GfxPBg==
- dependencies:
- "@webassemblyjs/ast" "1.12.1"
- "@webassemblyjs/helper-buffer" "1.12.1"
- "@webassemblyjs/wasm-gen" "1.12.1"
- "@webassemblyjs/wasm-parser" "1.12.1"
-
-"@webassemblyjs/wasm-parser@1.12.1", "@webassemblyjs/wasm-parser@^1.12.1":
- version "1.12.1"
- resolved "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.12.1.tgz"
- integrity sha512-xikIi7c2FHXysxXe3COrVUPSheuBtpcfhbpFj4gmu7KRLYOzANztwUU0IbsqvMqzuNK2+glRGWCEqZo1WCLyAQ==
- dependencies:
- "@webassemblyjs/ast" "1.12.1"
- "@webassemblyjs/helper-api-error" "1.11.6"
- "@webassemblyjs/helper-wasm-bytecode" "1.11.6"
- "@webassemblyjs/ieee754" "1.11.6"
- "@webassemblyjs/leb128" "1.11.6"
- "@webassemblyjs/utf8" "1.11.6"
-
-"@webassemblyjs/wast-printer@1.12.1":
- version "1.12.1"
- resolved "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.12.1.tgz"
- integrity sha512-+X4WAlOisVWQMikjbcvY2e0rwPsKQ9F688lksZhBcPycBBuii3O7m8FACbDMWDojpAqvjIncrG8J0XHKyQfVeA==
- dependencies:
- "@webassemblyjs/ast" "1.12.1"
- "@xtuc/long" "4.2.2"
-
-"@webpack-cli/configtest@^2.1.1":
- version "2.1.1"
- resolved "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-2.1.1.tgz"
- integrity sha512-wy0mglZpDSiSS0XHrVR+BAdId2+yxPSoJW8fsna3ZpYSlufjvxnP4YbKTCBZnNIcGN4r6ZPXV55X4mYExOfLmw==
-
-"@webpack-cli/info@^2.0.2":
- version "2.0.2"
- resolved "https://registry.npmjs.org/@webpack-cli/info/-/info-2.0.2.tgz"
- integrity sha512-zLHQdI/Qs1UyT5UBdWNqsARasIA+AaF8t+4u2aS2nEpBQh2mWIVb8qAklq0eUENnC5mOItrIB4LiS9xMtph18A==
-
-"@webpack-cli/serve@^2.0.5":
- version "2.0.5"
- resolved "https://registry.npmjs.org/@webpack-cli/serve/-/serve-2.0.5.tgz"
- integrity sha512-lqaoKnRYBdo1UgDX8uF24AfGMifWK19TxPmM5FHc2vAGxrJ/qtyUyFBWoY1tISZdelsQ5fBcOusifo5o5wSJxQ==
-
-"@xtuc/ieee754@^1.2.0":
- version "1.2.0"
- resolved "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz"
- integrity sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==
-
-"@xtuc/long@4.2.2":
- version "4.2.2"
- resolved "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz"
- integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==
-
-acorn-import-attributes@^1.9.5:
- version "1.9.5"
- resolved "https://registry.npmjs.org/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz"
- integrity sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==
-
-acorn@^8.7.1, acorn@^8.8.2:
- version "8.12.1"
- resolved "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz"
- integrity sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==
-
-ajv-keywords@^3.5.2:
- version "3.5.2"
- resolved "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz"
- integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==
-
-ajv@^6.12.5:
- version "6.12.6"
- resolved "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz"
- integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==
- dependencies:
- fast-deep-equal "^3.1.1"
- fast-json-stable-stringify "^2.0.0"
- json-schema-traverse "^0.4.1"
- uri-js "^4.2.2"
-
-ansi-styles@^4.1.0:
- version "4.3.0"
- resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz"
- integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==
- dependencies:
- color-convert "^2.0.1"
-
-braces@^3.0.3:
- version "3.0.3"
- resolved "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz"
- integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==
- dependencies:
- fill-range "^7.1.1"
-
-browserslist@^4.21.10:
- version "4.23.2"
- resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.23.2.tgz"
- integrity sha512-qkqSyistMYdxAcw+CzbZwlBy8AGmS/eEWs+sEV5TnLRGDOL+C5M2EnH6tlZyg0YoAxGJAFKh61En9BR941GnHA==
- dependencies:
- caniuse-lite "^1.0.30001640"
- electron-to-chromium "^1.4.820"
- node-releases "^2.0.14"
- update-browserslist-db "^1.1.0"
-
-buffer-from@^1.0.0:
- version "1.1.2"
- resolved "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz"
- integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==
-
-caniuse-lite@^1.0.30001640:
- version "1.0.30001643"
- resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001643.tgz"
- integrity sha512-ERgWGNleEilSrHM6iUz/zJNSQTP8Mr21wDWpdgvRwcTXGAq6jMtOUPP4dqFPTdKqZ2wKTdtB+uucZ3MRpAUSmg==
-
-chalk@^4.1.0:
- version "4.1.2"
- resolved "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz"
- integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==
- dependencies:
- ansi-styles "^4.1.0"
- supports-color "^7.1.0"
-
-chrome-trace-event@^1.0.2:
- version "1.0.4"
- resolved "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz"
- integrity sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==
-
-clone-deep@^4.0.1:
- version "4.0.1"
- resolved "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz"
- integrity sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==
- dependencies:
- is-plain-object "^2.0.4"
- kind-of "^6.0.2"
- shallow-clone "^3.0.0"
-
-color-convert@^2.0.1:
- version "2.0.1"
- resolved "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz"
- integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==
- dependencies:
- color-name "~1.1.4"
-
-color-name@~1.1.4:
- version "1.1.4"
- resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz"
- integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
-
-colorette@^2.0.14:
- version "2.0.20"
- resolved "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz"
- integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==
-
-commander@^10.0.1:
- version "10.0.1"
- resolved "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz"
- integrity sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==
-
-commander@^2.20.0:
- version "2.20.3"
- resolved "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz"
- integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==
-
-cross-spawn@^7.0.3:
- version "7.0.6"
- resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f"
- integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==
- dependencies:
- path-key "^3.1.0"
- shebang-command "^2.0.0"
- which "^2.0.1"
-
-electron-to-chromium@^1.4.820:
- version "1.5.0"
- resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.0.tgz"
- integrity sha512-Vb3xHHYnLseK8vlMJQKJYXJ++t4u1/qJ3vykuVrVjvdiOEhYyT1AuP4x03G8EnPmYvYOhe9T+dADTmthjRQMkA==
-
-enhanced-resolve@^5.0.0, enhanced-resolve@^5.17.1, enhanced-resolve@^5.7.0:
- version "5.17.1"
- resolved "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz"
- integrity sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==
- dependencies:
- graceful-fs "^4.2.4"
- tapable "^2.2.0"
-
-envinfo@^7.7.3:
- version "7.13.0"
- resolved "https://registry.npmjs.org/envinfo/-/envinfo-7.13.0.tgz"
- integrity sha512-cvcaMr7KqXVh4nyzGTVqTum+gAiL265x5jUWQIDLq//zOGbW+gSW/C+OWLleY/rs9Qole6AZLMXPbtIFQbqu+Q==
-
-es-module-lexer@^1.2.1:
- version "1.5.4"
- resolved "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.5.4.tgz"
- integrity sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw==
-
-escalade@^3.1.2:
- version "3.1.2"
- resolved "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz"
- integrity sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==
-
-eslint-scope@5.1.1:
- version "5.1.1"
- resolved "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz"
- integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==
- dependencies:
- esrecurse "^4.3.0"
- estraverse "^4.1.1"
-
-esrecurse@^4.3.0:
- version "4.3.0"
- resolved "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz"
- integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==
- dependencies:
- estraverse "^5.2.0"
-
-estraverse@^4.1.1:
- version "4.3.0"
- resolved "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz"
- integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==
-
-estraverse@^5.2.0:
- version "5.3.0"
- resolved "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz"
- integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==
-
-events@^3.2.0:
- version "3.3.0"
- resolved "https://registry.npmjs.org/events/-/events-3.3.0.tgz"
- integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==
-
-fast-deep-equal@^3.1.1:
- version "3.1.3"
- resolved "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz"
- integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==
-
-fast-json-stable-stringify@^2.0.0:
- version "2.1.0"
- resolved "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz"
- integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==
-
-fastest-levenshtein@^1.0.12:
- version "1.0.16"
- resolved "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz"
- integrity sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==
-
-fill-range@^7.1.1:
- version "7.1.1"
- resolved "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz"
- integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==
- dependencies:
- to-regex-range "^5.0.1"
-
-find-up@^4.0.0:
- version "4.1.0"
- resolved "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz"
- integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==
- dependencies:
- locate-path "^5.0.0"
- path-exists "^4.0.0"
-
-flat@^5.0.2:
- version "5.0.2"
- resolved "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz"
- integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==
-
-function-bind@^1.1.2:
- version "1.1.2"
- resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz"
- integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==
-
-glob-to-regexp@^0.4.1:
- version "0.4.1"
- resolved "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz"
- integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==
-
-graceful-fs@^4.1.2, graceful-fs@^4.2.11, graceful-fs@^4.2.4:
- version "4.2.11"
- resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz"
- integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==
-
-has-flag@^4.0.0:
- version "4.0.0"
- resolved "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz"
- integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==
-
-hasown@^2.0.2:
- version "2.0.2"
- resolved "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz"
- integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==
- dependencies:
- function-bind "^1.1.2"
-
-import-local@^3.0.2:
- version "3.2.0"
- resolved "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz"
- integrity sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==
- dependencies:
- pkg-dir "^4.2.0"
- resolve-cwd "^3.0.0"
-
-interpret@^3.1.1:
- version "3.1.1"
- resolved "https://registry.npmjs.org/interpret/-/interpret-3.1.1.tgz"
- integrity sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ==
-
-is-core-module@^2.13.0:
- version "2.15.0"
- resolved "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.0.tgz"
- integrity sha512-Dd+Lb2/zvk9SKy1TGCt1wFJFo/MWBPMX5x7KcvLajWTGuomczdQX61PvY5yK6SVACwpoexWo81IfFyoKY2QnTA==
- dependencies:
- hasown "^2.0.2"
-
-is-number@^7.0.0:
- version "7.0.0"
- resolved "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz"
- integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==
-
-is-plain-object@^2.0.4:
- version "2.0.4"
- resolved "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz"
- integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==
- dependencies:
- isobject "^3.0.1"
-
-isexe@^2.0.0:
- version "2.0.0"
- resolved "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz"
- integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==
-
-isobject@^3.0.1:
- version "3.0.1"
- resolved "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz"
- integrity sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==
-
-jest-worker@^27.4.5:
- version "27.5.1"
- resolved "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz"
- integrity sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==
- dependencies:
- "@types/node" "*"
- merge-stream "^2.0.0"
- supports-color "^8.0.0"
-
-json-parse-even-better-errors@^2.3.1:
- version "2.3.1"
- resolved "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz"
- integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==
-
-json-schema-traverse@^0.4.1:
- version "0.4.1"
- resolved "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz"
- integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==
-
-json5@^2.2.2:
- version "2.2.3"
- resolved "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz"
- integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==
-
-kind-of@^6.0.2:
- version "6.0.3"
- resolved "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz"
- integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==
-
-loader-runner@^4.2.0:
- version "4.3.0"
- resolved "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz"
- integrity sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==
-
-locate-path@^5.0.0:
- version "5.0.0"
- resolved "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz"
- integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==
- dependencies:
- p-locate "^4.1.0"
-
-merge-stream@^2.0.0:
- version "2.0.0"
- resolved "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz"
- integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==
-
-micromatch@^4.0.0:
- version "4.0.8"
- resolved "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz"
- integrity sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==
- dependencies:
- braces "^3.0.3"
- picomatch "^2.3.1"
-
-mime-db@1.52.0:
- version "1.52.0"
- resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz"
- integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==
-
-mime-types@^2.1.27:
- version "2.1.35"
- resolved "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz"
- integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==
- dependencies:
- mime-db "1.52.0"
-
-minimist@^1.2.6:
- version "1.2.8"
- resolved "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz"
- integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==
-
-neo-async@^2.6.2:
- version "2.6.2"
- resolved "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz"
- integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==
-
-node-releases@^2.0.14:
- version "2.0.18"
- resolved "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz"
- integrity sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==
-
-p-limit@^2.2.0:
- version "2.3.0"
- resolved "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz"
- integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==
- dependencies:
- p-try "^2.0.0"
-
-p-locate@^4.1.0:
- version "4.1.0"
- resolved "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz"
- integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==
- dependencies:
- p-limit "^2.2.0"
-
-p-try@^2.0.0:
- version "2.2.0"
- resolved "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz"
- integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==
-
-path-exists@^4.0.0:
- version "4.0.0"
- resolved "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz"
- integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==
-
-path-key@^3.1.0:
- version "3.1.1"
- resolved "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz"
- integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==
-
-path-parse@^1.0.7:
- version "1.0.7"
- resolved "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz"
- integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==
-
-picocolors@^1.0.1:
- version "1.0.1"
- resolved "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz"
- integrity sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==
-
-picomatch@^2.3.1:
- version "2.3.1"
- resolved "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz"
- integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
-
-pkg-dir@^4.2.0:
- version "4.2.0"
- resolved "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz"
- integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==
- dependencies:
- find-up "^4.0.0"
-
-punycode@^2.1.0:
- version "2.3.1"
- resolved "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz"
- integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==
-
-randombytes@^2.1.0:
- version "2.1.0"
- resolved "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz"
- integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==
- dependencies:
- safe-buffer "^5.1.0"
-
-rechoir@^0.8.0:
- version "0.8.0"
- resolved "https://registry.npmjs.org/rechoir/-/rechoir-0.8.0.tgz"
- integrity sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==
- dependencies:
- resolve "^1.20.0"
-
-resolve-cwd@^3.0.0:
- version "3.0.0"
- resolved "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz"
- integrity sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==
- dependencies:
- resolve-from "^5.0.0"
-
-resolve-from@^5.0.0:
- version "5.0.0"
- resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz"
- integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==
-
-resolve@^1.20.0:
- version "1.22.8"
- resolved "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz"
- integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==
- dependencies:
- is-core-module "^2.13.0"
- path-parse "^1.0.7"
- supports-preserve-symlinks-flag "^1.0.0"
-
-safe-buffer@^5.1.0:
- version "5.2.1"
- resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz"
- integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
-
-schema-utils@^3.1.1, schema-utils@^3.2.0:
- version "3.3.0"
- resolved "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz"
- integrity sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==
- dependencies:
- "@types/json-schema" "^7.0.8"
- ajv "^6.12.5"
- ajv-keywords "^3.5.2"
-
-semver@^7.3.4:
- version "7.6.3"
- resolved "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz"
- integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==
-
-serialize-javascript@^6.0.1:
- version "6.0.2"
- resolved "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz"
- integrity sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==
- dependencies:
- randombytes "^2.1.0"
-
-shallow-clone@^3.0.0:
- version "3.0.1"
- resolved "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz"
- integrity sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==
- dependencies:
- kind-of "^6.0.2"
-
-shebang-command@^2.0.0:
- version "2.0.0"
- resolved "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz"
- integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==
- dependencies:
- shebang-regex "^3.0.0"
-
-shebang-regex@^3.0.0:
- version "3.0.0"
- resolved "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz"
- integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==
-
-source-map-support@~0.5.20:
- version "0.5.21"
- resolved "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz"
- integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==
- dependencies:
- buffer-from "^1.0.0"
- source-map "^0.6.0"
-
-source-map@^0.6.0:
- version "0.6.1"
- resolved "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz"
- integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
-
-source-map@^0.7.4:
- version "0.7.4"
- resolved "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz"
- integrity sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==
-
-strip-bom@^3.0.0:
- version "3.0.0"
- resolved "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz"
- integrity sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==
-
-supports-color@^7.1.0:
- version "7.2.0"
- resolved "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz"
- integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==
- dependencies:
- has-flag "^4.0.0"
-
-supports-color@^8.0.0:
- version "8.1.1"
- resolved "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz"
- integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==
- dependencies:
- has-flag "^4.0.0"
-
-supports-preserve-symlinks-flag@^1.0.0:
- version "1.0.0"
- resolved "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz"
- integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==
-
-tapable@^2.1.1, tapable@^2.2.0:
- version "2.2.1"
- resolved "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz"
- integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==
-
-terser-webpack-plugin@^5.3.10:
- version "5.3.10"
- resolved "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.10.tgz"
- integrity sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w==
- dependencies:
- "@jridgewell/trace-mapping" "^0.3.20"
- jest-worker "^27.4.5"
- schema-utils "^3.1.1"
- serialize-javascript "^6.0.1"
- terser "^5.26.0"
-
-terser@^5.26.0:
- version "5.31.3"
- resolved "https://registry.npmjs.org/terser/-/terser-5.31.3.tgz"
- integrity sha512-pAfYn3NIZLyZpa83ZKigvj6Rn9c/vd5KfYGX7cN1mnzqgDcxWvrU5ZtAfIKhEXz9nRecw4z3LXkjaq96/qZqAA==
- dependencies:
- "@jridgewell/source-map" "^0.3.3"
- acorn "^8.8.2"
- commander "^2.20.0"
- source-map-support "~0.5.20"
-
-to-regex-range@^5.0.1:
- version "5.0.1"
- resolved "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz"
- integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==
- dependencies:
- is-number "^7.0.0"
-
-ts-loader@^9.4.4:
- version "9.5.1"
- resolved "https://registry.npmjs.org/ts-loader/-/ts-loader-9.5.1.tgz"
- integrity sha512-rNH3sK9kGZcH9dYzC7CewQm4NtxJTjSEVRJ2DyBZR7f8/wcta+iV44UPCXc5+nzDzivKtlzV6c9P4e+oFhDLYg==
- dependencies:
- chalk "^4.1.0"
- enhanced-resolve "^5.0.0"
- micromatch "^4.0.0"
- semver "^7.3.4"
- source-map "^0.7.4"
-
-tsconfig-paths-webpack-plugin@^4.1.0:
- version "4.1.0"
- resolved "https://registry.npmjs.org/tsconfig-paths-webpack-plugin/-/tsconfig-paths-webpack-plugin-4.1.0.tgz"
- integrity sha512-xWFISjviPydmtmgeUAuXp4N1fky+VCtfhOkDUFIv5ea7p4wuTomI4QTrXvFBX2S4jZsmyTSrStQl+E+4w+RzxA==
- dependencies:
- chalk "^4.1.0"
- enhanced-resolve "^5.7.0"
- tsconfig-paths "^4.1.2"
-
-tsconfig-paths@^4.1.2:
- version "4.2.0"
- resolved "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz"
- integrity sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==
- dependencies:
- json5 "^2.2.2"
- minimist "^1.2.6"
- strip-bom "^3.0.0"
-
-typescript@5.4.2:
- version "5.4.2"
- resolved "https://registry.npmjs.org/typescript/-/typescript-5.4.2.tgz"
- integrity sha512-+2/g0Fds1ERlP6JsakQQDXjZdZMM+rqpamFZJEKh4kwTIn3iDkgKtby0CeNd5ATNZ4Ry1ax15TMx0W2V+miizQ==
-
-undici-types@~5.26.4:
- version "5.26.5"
- resolved "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz"
- integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==
-
-update-browserslist-db@^1.1.0:
- version "1.1.0"
- resolved "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz"
- integrity sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==
- dependencies:
- escalade "^3.1.2"
- picocolors "^1.0.1"
-
-uri-js@^4.2.2:
- version "4.4.1"
- resolved "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz"
- integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==
- dependencies:
- punycode "^2.1.0"
-
-watchpack@^2.4.1:
- version "2.4.1"
- resolved "https://registry.npmjs.org/watchpack/-/watchpack-2.4.1.tgz"
- integrity sha512-8wrBCMtVhqcXP2Sup1ctSkga6uc2Bx0IIvKyT7yTFier5AXHooSI+QyQQAtTb7+E0IUCCKyTFmXqdqgum2XWGg==
- dependencies:
- glob-to-regexp "^0.4.1"
- graceful-fs "^4.1.2"
-
-webpack-cli@^5.1.4:
- version "5.1.4"
- resolved "https://registry.npmjs.org/webpack-cli/-/webpack-cli-5.1.4.tgz"
- integrity sha512-pIDJHIEI9LR0yxHXQ+Qh95k2EvXpWzZ5l+d+jIo+RdSm9MiHfzazIxwwni/p7+x4eJZuvG1AJwgC4TNQ7NRgsg==
- dependencies:
- "@discoveryjs/json-ext" "^0.5.0"
- "@webpack-cli/configtest" "^2.1.1"
- "@webpack-cli/info" "^2.0.2"
- "@webpack-cli/serve" "^2.0.5"
- colorette "^2.0.14"
- commander "^10.0.1"
- cross-spawn "^7.0.3"
- envinfo "^7.7.3"
- fastest-levenshtein "^1.0.12"
- import-local "^3.0.2"
- interpret "^3.1.1"
- rechoir "^0.8.0"
- webpack-merge "^5.7.3"
-
-webpack-merge@^5.7.3:
- version "5.10.0"
- resolved "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.10.0.tgz"
- integrity sha512-+4zXKdx7UnO+1jaN4l2lHVD+mFvnlZQP/6ljaJVb4SZiwIKeUnrT5l0gkT8z+n4hKpC+jpOv6O9R+gLtag7pSA==
- dependencies:
- clone-deep "^4.0.1"
- flat "^5.0.2"
- wildcard "^2.0.0"
-
-webpack-sources@^3.2.3:
- version "3.2.3"
- resolved "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz"
- integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==
-
-webpack@^5.94.0:
- version "5.94.0"
- resolved "https://registry.npmjs.org/webpack/-/webpack-5.94.0.tgz"
- integrity sha512-KcsGn50VT+06JH/iunZJedYGUJS5FGjow8wb9c0v5n1Om8O1g4L6LjtfxwlXIATopoQu+vOXXa7gYisWxCoPyg==
- dependencies:
- "@types/estree" "^1.0.5"
- "@webassemblyjs/ast" "^1.12.1"
- "@webassemblyjs/wasm-edit" "^1.12.1"
- "@webassemblyjs/wasm-parser" "^1.12.1"
- acorn "^8.7.1"
- acorn-import-attributes "^1.9.5"
- browserslist "^4.21.10"
- chrome-trace-event "^1.0.2"
- enhanced-resolve "^5.17.1"
- es-module-lexer "^1.2.1"
- eslint-scope "5.1.1"
- events "^3.2.0"
- glob-to-regexp "^0.4.1"
- graceful-fs "^4.2.11"
- json-parse-even-better-errors "^2.3.1"
- loader-runner "^4.2.0"
- mime-types "^2.1.27"
- neo-async "^2.6.2"
- schema-utils "^3.2.0"
- tapable "^2.1.1"
- terser-webpack-plugin "^5.3.10"
- watchpack "^2.4.1"
- webpack-sources "^3.2.3"
-
-which@^2.0.1:
- version "2.0.2"
- resolved "https://registry.npmjs.org/which/-/which-2.0.2.tgz"
- integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==
- dependencies:
- isexe "^2.0.0"
-
-wildcard@^2.0.0:
- version "2.0.1"
- resolved "https://registry.npmjs.org/wildcard/-/wildcard-2.0.1.tgz"
- integrity sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==
diff --git a/toolkit/chakra/accordion.tsx b/toolkit/chakra/accordion.tsx
new file mode 100644
index 0000000000..55d926ea52
--- /dev/null
+++ b/toolkit/chakra/accordion.tsx
@@ -0,0 +1,91 @@
+import { Accordion } from '@chakra-ui/react';
+import * as React from 'react';
+
+import IconSvg from 'ui/shared/IconSvg';
+
+interface AccordionItemTriggerProps extends Accordion.ItemTriggerProps {
+ indicatorPlacement?: 'start' | 'end';
+ noIndicator?: boolean;
+ variant?: Accordion.RootProps['variant'];
+}
+
+export const AccordionItemTrigger = React.forwardRef<
+ HTMLButtonElement,
+ AccordionItemTriggerProps
+>(function AccordionItemTrigger(props, ref) {
+ const { children, indicatorPlacement: indicatorPlacementProp, variant, noIndicator, ...rest } = props;
+
+ const indicatorPlacement = variant === 'faq' ? 'start' : (indicatorPlacementProp ?? 'end');
+
+ const indicator = variant === 'faq' ? (
+
+
+
+ ) : (
+
+
+
+ );
+
+ return (
+
+ { indicatorPlacement === 'start' && !noIndicator && indicator }
+ { children }
+ { indicatorPlacement === 'end' && !noIndicator && indicator }
+
+ );
+});
+
+interface AccordionItemContentProps extends Accordion.ItemContentProps {}
+
+export const AccordionItemContent = React.forwardRef<
+ HTMLDivElement,
+ AccordionItemContentProps
+>(function AccordionItemContent(props, ref) {
+ return (
+
+
+
+ );
+});
+
+export const AccordionRoot = (props: Accordion.RootProps) => {
+ const { multiple = true, ...rest } = props;
+ return ;
+};
+
+export const AccordionItem = Accordion.Item;
diff --git a/toolkit/chakra/alert.tsx b/toolkit/chakra/alert.tsx
new file mode 100644
index 0000000000..bb509d3265
--- /dev/null
+++ b/toolkit/chakra/alert.tsx
@@ -0,0 +1,88 @@
+import type { AlertDescriptionProps } from '@chakra-ui/react';
+import { Alert as ChakraAlert } from '@chakra-ui/react';
+import * as React from 'react';
+
+import IconSvg from 'ui/shared/IconSvg';
+
+import { CloseButton } from './close-button';
+import { Skeleton } from './skeleton';
+
+export interface AlertProps extends Omit {
+ startElement?: React.ReactNode;
+ endElement?: React.ReactNode;
+ descriptionProps?: AlertDescriptionProps;
+ title?: React.ReactNode;
+ icon?: React.ReactElement;
+ closable?: boolean;
+ onClose?: () => void;
+ loading?: boolean;
+ showIcon?: boolean;
+}
+
+export const Alert = React.forwardRef(
+ function Alert(props, ref) {
+ const {
+ title,
+ children,
+ icon,
+ closable,
+ onClose,
+ startElement,
+ endElement,
+ loading,
+ showIcon = false,
+ descriptionProps,
+ ...rest
+ } = props;
+
+ const [ isOpen, setIsOpen ] = React.useState(true);
+
+ const defaultIcon = ;
+
+ const iconElement = (() => {
+ if (startElement !== undefined) {
+ return startElement;
+ }
+
+ if (!showIcon && icon === undefined) {
+ return null;
+ }
+
+ return { icon || defaultIcon } ;
+ })();
+
+ const handleClose = React.useCallback(() => {
+ setIsOpen(false);
+ onClose?.();
+ }, [ onClose ]);
+
+ if (closable && !isOpen) {
+ return null;
+ }
+
+ return (
+
+
+ { iconElement }
+ { children ? (
+
+ { title && { title } }
+ { children }
+
+ ) : (
+ { title }
+ ) }
+ { endElement }
+ { closable && (
+
+ ) }
+
+
+ );
+ },
+);
diff --git a/toolkit/chakra/avatar.tsx b/toolkit/chakra/avatar.tsx
new file mode 100644
index 0000000000..9d323b2e8d
--- /dev/null
+++ b/toolkit/chakra/avatar.tsx
@@ -0,0 +1,74 @@
+'use client';
+
+import type { GroupProps, SlotRecipeProps } from '@chakra-ui/react';
+import { Avatar as ChakraAvatar, Group } from '@chakra-ui/react';
+import * as React from 'react';
+
+type ImageProps = React.ImgHTMLAttributes;
+
+export interface AvatarProps extends ChakraAvatar.RootProps {
+ name?: string;
+ src?: string;
+ srcSet?: string;
+ loading?: ImageProps['loading'];
+ icon?: React.ReactElement;
+ fallback?: React.ReactNode;
+}
+
+export const Avatar = React.forwardRef(
+ function Avatar(props, ref) {
+ const { name, src, srcSet, loading, icon, fallback, children, ...rest } =
+ props;
+ return (
+
+
+ { fallback }
+
+
+ { children }
+
+ );
+ },
+);
+
+interface AvatarFallbackProps extends ChakraAvatar.FallbackProps {
+ name?: string;
+ icon?: React.ReactElement;
+}
+
+const AvatarFallback = React.forwardRef(
+ function AvatarFallback(props, ref) {
+ const { name, icon, children, ...rest } = props;
+ return (
+
+ { children }
+ { name != null && children == null && <>{ getInitials(name) }> }
+ { name == null && children == null && (
+ { icon }
+ ) }
+
+ );
+ },
+);
+
+function getInitials(name: string) {
+ const names = name.trim().split(' ');
+ const firstName = names[0] != null ? names[0] : '';
+ const lastName = names.length > 1 ? names[names.length - 1] : '';
+ return firstName && lastName ?
+ `${ firstName.charAt(0) }${ lastName.charAt(0) }` :
+ firstName.charAt(0);
+}
+
+interface AvatarGroupProps extends GroupProps, SlotRecipeProps<'avatar'> {}
+
+export const AvatarGroup = React.forwardRef(
+ function AvatarGroup(props, ref) {
+ const { size, variant, borderless, ...rest } = props;
+ return (
+
+
+
+ );
+ },
+);
diff --git a/toolkit/chakra/badge.tsx b/toolkit/chakra/badge.tsx
new file mode 100644
index 0000000000..868866b0fc
--- /dev/null
+++ b/toolkit/chakra/badge.tsx
@@ -0,0 +1,39 @@
+import type { BadgeProps as ChakraBadgeProps } from '@chakra-ui/react';
+import { chakra, Badge as ChakraBadge } from '@chakra-ui/react';
+import React from 'react';
+
+import type { IconName } from 'ui/shared/IconSvg';
+import IconSvg from 'ui/shared/IconSvg';
+import TruncatedTextTooltip from 'ui/shared/TruncatedTextTooltip';
+
+import { Skeleton } from './skeleton';
+
+export interface BadgeProps extends Omit {
+ loading?: boolean;
+ iconStart?: IconName;
+ endElement?: React.ReactNode;
+ truncated?: boolean;
+}
+
+export const Badge = React.forwardRef(
+ function Badge(props, ref) {
+ const { loading, iconStart, children, asChild = true, truncated = false, endElement, ...rest } = props;
+
+ const child = { children } ;
+
+ const childrenElement = truncated ? (
+
+ { child }
+
+ ) : child;
+
+ return (
+
+
+ { iconStart && }
+ { childrenElement }
+ { endElement }
+
+
+ );
+ });
diff --git a/toolkit/chakra/button.tsx b/toolkit/chakra/button.tsx
new file mode 100644
index 0000000000..91cb0dc63a
--- /dev/null
+++ b/toolkit/chakra/button.tsx
@@ -0,0 +1,134 @@
+import type { ButtonProps as ChakraButtonProps, ButtonGroupProps as ChakraButtonGroupProps } from '@chakra-ui/react';
+import {
+ AbsoluteCenter,
+ Button as ChakraButton,
+ ButtonGroup as ChakraButtonGroup,
+ Span,
+ Spinner,
+} from '@chakra-ui/react';
+import * as React from 'react';
+
+import { Skeleton } from './skeleton';
+
+interface ButtonLoadingProps {
+ loading?: boolean;
+ loadingText?: React.ReactNode;
+ loadingSkeleton?: boolean;
+}
+
+export interface ButtonProps extends ChakraButtonProps, ButtonLoadingProps {
+ expanded?: boolean;
+ selected?: boolean;
+ highlighted?: boolean;
+}
+
+export const Button = React.forwardRef(
+ function Button(props, ref) {
+ const { loading, disabled, loadingText, children, expanded, selected, highlighted, loadingSkeleton = false, ...rest } = props;
+
+ const content = (() => {
+ if (loading && !loadingText) {
+ return (
+ <>
+
+
+
+ { children }
+ >
+ );
+ }
+
+ if (loading && loadingText) {
+ return (
+ <>
+
+ { loadingText }
+ >
+ );
+ }
+
+ return children;
+ })();
+
+ return (
+ }>
+
+ { content }
+
+
+ );
+ },
+);
+
+export interface ButtonGroupProps extends ChakraButtonGroupProps {}
+
+export const ButtonGroup = React.forwardRef(
+ function ButtonGroup(props, ref) {
+ const { ...rest } = props;
+
+ return (
+
+ );
+ },
+);
+
+export interface ButtonGroupRadioProps extends Omit {
+ children: Array>;
+ onChange?: (value: string) => void;
+ defaultValue?: string;
+ loading?: boolean;
+ equalWidth?: boolean;
+}
+
+export const ButtonGroupRadio = React.forwardRef(
+ function ButtonGroupRadio(props, ref) {
+ const { children, onChange, variant = 'segmented', defaultValue, loading = false, equalWidth = false, ...rest } = props;
+
+ const firstChildValue = React.useMemo(() => {
+ const firstChild = Array.isArray(children) ? children[0] : undefined;
+ return typeof firstChild?.props.value === 'string' ? firstChild.props.value : undefined;
+ }, [ children ]);
+
+ const [ value, setValue ] = React.useState(defaultValue ?? firstChildValue);
+
+ const handleItemClick = React.useCallback((event: React.MouseEvent) => {
+ const value = event.currentTarget.value;
+ setValue(value);
+ onChange?.(value);
+ }, [ onChange ]);
+
+ const clonedChildren = React.Children.map(children, (child: React.ReactElement) => {
+ return React.cloneElement(child, {
+ onClick: handleItemClick,
+ selected: value === child.props.value,
+ variant,
+ });
+ });
+
+ const childrenLength = React.Children.count(children);
+
+ return (
+
+
+ { clonedChildren }
+
+
+ );
+ },
+);
diff --git a/toolkit/chakra/checkbox.tsx b/toolkit/chakra/checkbox.tsx
new file mode 100644
index 0000000000..ed01adff12
--- /dev/null
+++ b/toolkit/chakra/checkbox.tsx
@@ -0,0 +1,49 @@
+import type { Checkbox as ArkCheckbox } from '@ark-ui/react/checkbox';
+import type { HTMLChakraProps } from '@chakra-ui/react';
+import { Checkbox as ChakraCheckbox, CheckboxGroup as ChakraCheckboxGroup } from '@chakra-ui/react';
+import * as React from 'react';
+
+export interface CheckboxProps extends ChakraCheckbox.RootProps {
+ icon?: React.ReactNode;
+ inputProps?: React.InputHTMLAttributes;
+ rootRef?: React.Ref;
+}
+
+export const Checkbox = React.forwardRef(
+ function Checkbox(props, ref) {
+ const { icon, children, inputProps, rootRef, ...rest } = props;
+ return (
+
+
+
+ { icon || }
+
+ { children != null && (
+ { children }
+ ) }
+
+ );
+ },
+);
+
+export interface CheckboxGroupProps extends HTMLChakraProps<'div', ArkCheckbox.GroupProps> {
+ orientation?: 'vertical' | 'horizontal';
+}
+
+export const CheckboxGroup = React.forwardRef(
+ function CheckboxGroup(props, ref) {
+ const { children, orientation = 'vertical', ...rest } = props;
+ return (
+
+ { children }
+
+ );
+ },
+);
diff --git a/toolkit/chakra/close-button.tsx b/toolkit/chakra/close-button.tsx
new file mode 100644
index 0000000000..cdd4b7b116
--- /dev/null
+++ b/toolkit/chakra/close-button.tsx
@@ -0,0 +1,27 @@
+import type { ButtonProps } from '@chakra-ui/react';
+import { useRecipe } from '@chakra-ui/react';
+import * as React from 'react';
+
+import IconSvg from 'ui/shared/IconSvg';
+
+import { recipe as closeButtonRecipe } from '../theme/recipes/close-button.recipe';
+import { IconButton } from './icon-button';
+export interface CloseButtonProps extends Omit {
+ variant?: 'plain';
+ size?: 'md';
+}
+
+export const CloseButton = React.forwardRef<
+ HTMLButtonElement,
+ CloseButtonProps
+>(function CloseButton(props, ref) {
+ const recipe = useRecipe({ recipe: closeButtonRecipe });
+ const [ recipeProps, restProps ] = recipe.splitVariantProps(props);
+ const styles = recipe(recipeProps);
+
+ return (
+
+ { props.children ?? }
+
+ );
+});
diff --git a/toolkit/chakra/collapsible.tsx b/toolkit/chakra/collapsible.tsx
new file mode 100644
index 0000000000..cb6b2cc1d7
--- /dev/null
+++ b/toolkit/chakra/collapsible.tsx
@@ -0,0 +1,100 @@
+import { Flex, type FlexProps } from '@chakra-ui/react';
+import React from 'react';
+import { scroller, Element } from 'react-scroll';
+
+import useUpdateEffect from 'lib/hooks/useUpdateEffect';
+
+import type { LinkProps } from './link';
+import { Link } from './link';
+
+interface CollapsibleDetailsProps extends LinkProps {
+ children: React.ReactNode;
+ id?: string;
+ isExpanded?: boolean;
+ text?: [string, string];
+ noScroll?: boolean;
+}
+
+const SCROLL_CONFIG = {
+ duration: 500,
+ smooth: true,
+};
+
+const CUT_ID = 'CollapsibleDetails';
+
+export const CollapsibleDetails = (props: CollapsibleDetailsProps) => {
+
+ const { children, id = CUT_ID, onClick, isExpanded: isExpandedProp = false, text: textProp, loading, noScroll, ...rest } = props;
+
+ const [ isExpanded, setIsExpanded ] = React.useState(isExpandedProp);
+
+ const handleClick = React.useCallback((event: React.MouseEvent) => {
+ setIsExpanded((flag) => !flag);
+ if (!noScroll) {
+ scroller.scrollTo(id, SCROLL_CONFIG);
+ }
+ onClick?.(event);
+ }, [ id, noScroll, onClick ]);
+
+ useUpdateEffect(() => {
+ setIsExpanded(isExpandedProp);
+ isExpandedProp && !noScroll && scroller.scrollTo(id, SCROLL_CONFIG);
+ }, [ isExpandedProp, id, noScroll ]);
+
+ const text = isExpanded ? (textProp?.[1] ?? 'Hide details') : (textProp?.[0] ?? 'View details');
+
+ return (
+ <>
+
+ { text }
+
+ { isExpanded && children }
+ >
+ );
+};
+
+interface CollapsibleListProps extends FlexProps {
+ items: Array;
+ renderItem: (item: T, index: number) => React.ReactNode;
+ triggerProps?: LinkProps;
+ cutLength?: number;
+}
+
+export const CollapsibleList = (props: CollapsibleListProps) => {
+ const CUT_LENGTH = 3;
+
+ const { items, renderItem, triggerProps, cutLength = CUT_LENGTH, ...rest } = props;
+
+ const [ isExpanded, setIsExpanded ] = React.useState(false);
+
+ const handleToggle = React.useCallback(() => {
+ setIsExpanded((flag) => !flag);
+ }, []);
+
+ return (
+
+ { items.slice(0, isExpanded ? undefined : cutLength).map(renderItem) }
+ { items.length > cutLength && (
+
+ { isExpanded ? 'Hide' : 'Show all' }
+
+ ) }
+
+ );
+};
diff --git a/toolkit/chakra/color-mode.tsx b/toolkit/chakra/color-mode.tsx
new file mode 100644
index 0000000000..ea39a3afb1
--- /dev/null
+++ b/toolkit/chakra/color-mode.tsx
@@ -0,0 +1,40 @@
+'use client';
+
+import { ThemeProvider, useTheme } from 'next-themes';
+import type { ThemeProviderProps } from 'next-themes';
+import * as React from 'react';
+
+import config from 'configs/app';
+
+export interface ColorModeProviderProps extends ThemeProviderProps {}
+
+export type ColorMode = 'light' | 'dark';
+
+export function ColorModeProvider(props: ColorModeProviderProps) {
+ return (
+
+ );
+}
+
+export function useColorMode() {
+ const { resolvedTheme, setTheme } = useTheme();
+ const toggleColorMode = () => {
+ setTheme(resolvedTheme === 'light' ? 'dark' : 'light');
+ };
+ return {
+ colorMode: resolvedTheme as ColorMode,
+ setColorMode: setTheme,
+ toggleColorMode,
+ };
+}
+
+export function useColorModeValue(light: T, dark: T) {
+ const { colorMode } = useColorMode();
+ return colorMode === 'light' ? light : dark;
+}
diff --git a/toolkit/chakra/dialog.tsx b/toolkit/chakra/dialog.tsx
new file mode 100644
index 0000000000..3f93e301f1
--- /dev/null
+++ b/toolkit/chakra/dialog.tsx
@@ -0,0 +1,83 @@
+import { Dialog as ChakraDialog, Portal } from '@chakra-ui/react';
+import * as React from 'react';
+
+import ButtonBackTo from 'ui/shared/buttons/ButtonBackTo';
+
+import { CloseButton } from './close-button';
+
+interface DialogContentProps extends ChakraDialog.ContentProps {
+ portalled?: boolean;
+ portalRef?: React.RefObject;
+ backdrop?: boolean;
+}
+
+export const DialogContent = React.forwardRef<
+ HTMLDivElement,
+ DialogContentProps
+>(function DialogContent(props, ref) {
+ const {
+ children,
+ portalled = true,
+ portalRef,
+ backdrop = true,
+ ...rest
+ } = props;
+
+ return (
+
+ { backdrop && }
+
+
+ { children }
+
+
+
+ );
+});
+
+export const DialogCloseTrigger = React.forwardRef<
+ HTMLButtonElement,
+ ChakraDialog.CloseTriggerProps
+>(function DialogCloseTrigger(props, ref) {
+ return (
+
+
+ { props.children }
+
+
+ );
+});
+
+export interface DialogHeaderProps extends ChakraDialog.HeaderProps {
+ startElement?: React.ReactNode;
+ onBackToClick?: () => void;
+}
+
+export const DialogHeader = React.forwardRef<
+ HTMLDivElement,
+ DialogHeaderProps
+>(function DialogHeader(props, ref) {
+ const { startElement: startElementProp, onBackToClick, ...rest } = props;
+
+ const startElement = startElementProp ?? (onBackToClick && );
+
+ return (
+
+ { startElement }
+ { props.children }
+
+
+ );
+});
+
+export const DialogRoot = ChakraDialog.Root;
+export const DialogFooter = ChakraDialog.Footer;
+export const DialogBody = ChakraDialog.Body;
+export const DialogBackdrop = ChakraDialog.Backdrop;
+export const DialogTitle = ChakraDialog.Title;
+export const DialogDescription = ChakraDialog.Description;
+export const DialogTrigger = ChakraDialog.Trigger;
+export const DialogActionTrigger = ChakraDialog.ActionTrigger;
diff --git a/toolkit/chakra/drawer.tsx b/toolkit/chakra/drawer.tsx
new file mode 100644
index 0000000000..75081fa3d0
--- /dev/null
+++ b/toolkit/chakra/drawer.tsx
@@ -0,0 +1,64 @@
+import { Drawer as ChakraDrawer, Portal } from '@chakra-ui/react';
+import * as React from 'react';
+
+import { CloseButton } from './close-button';
+
+interface DrawerContentProps extends ChakraDrawer.ContentProps {
+ portalled?: boolean;
+ portalRef?: React.RefObject;
+ offset?: ChakraDrawer.ContentProps['padding'];
+ backdrop?: boolean;
+}
+
+export const DrawerContent = React.forwardRef<
+ HTMLDivElement,
+ DrawerContentProps
+>(function DrawerContent(props, ref) {
+ const { children, portalled = true, portalRef, offset, backdrop = true, ...rest } = props;
+ return (
+
+ { backdrop && }
+
+
+ { children }
+
+
+
+ );
+});
+
+export const DrawerCloseTrigger = React.forwardRef<
+ HTMLButtonElement,
+ ChakraDrawer.CloseTriggerProps
+>(function DrawerCloseTrigger(props, ref) {
+ return (
+
+
+
+ );
+});
+
+const EMPTY_ELEMENT = () => null;
+
+export const DrawerRoot = (props: ChakraDrawer.RootProps) => {
+ const { initialFocusEl = EMPTY_ELEMENT, lazyMount = true, unmountOnExit = true, ...rest } = props;
+ return ;
+};
+
+export const DrawerTrigger = (props: ChakraDrawer.TriggerProps) => {
+ const { asChild = true, ...rest } = props;
+ return ;
+};
+
+export const DrawerFooter = ChakraDrawer.Footer;
+export const DrawerHeader = ChakraDrawer.Header;
+export const DrawerBody = ChakraDrawer.Body;
+export const DrawerDescription = ChakraDrawer.Description;
+export const DrawerTitle = ChakraDrawer.Title;
+export const DrawerActionTrigger = ChakraDrawer.ActionTrigger;
diff --git a/toolkit/chakra/field.tsx b/toolkit/chakra/field.tsx
new file mode 100644
index 0000000000..e5c8db38bb
--- /dev/null
+++ b/toolkit/chakra/field.tsx
@@ -0,0 +1,108 @@
+import { Field as ChakraField } from '@chakra-ui/react';
+import * as React from 'react';
+
+import { space } from 'lib/html-entities';
+
+import getComponentDisplayName from '../utils/getComponentDisplayName';
+import type { InputProps } from './input';
+import type { InputGroupProps } from './input-group';
+
+export interface FieldProps extends Omit {
+ label?: React.ReactNode;
+ helperText?: React.ReactNode;
+ errorText?: React.ReactNode;
+ optionalText?: React.ReactNode;
+ children: React.ReactElement | React.ReactElement;
+ size?: 'sm' | 'md' | 'lg' | '2xl';
+}
+
+export const Field = React.forwardRef(
+ function Field(props, ref) {
+ const { label, children, helperText, errorText, optionalText, ...rest } = props;
+
+ // A floating field cannot be without a label.
+ if (rest.floating && label) {
+ const injectedProps = {
+ className: 'peer',
+ placeholder: ' ',
+ size: rest.size,
+ floating: rest.floating,
+ bgColor: rest.bgColor,
+ disabled: rest.disabled,
+ readOnly: rest.readOnly,
+ };
+
+ const labelElement = (
+
+ { label }
+
+ { errorText && (
+ -{ space }{ errorText }
+ ) }
+
+ );
+
+ const helperTextElement = helperText && (
+ { helperText }
+ );
+
+ const child = React.Children.only>(children);
+ const isInputGroup = getComponentDisplayName(child.type) === 'InputGroup';
+
+ if (isInputGroup) {
+ const inputElement = React.cloneElement(
+ React.Children.only>(child.props.children as React.ReactElement),
+ injectedProps,
+ );
+
+ const groupInputElement = React.cloneElement(child,
+ {},
+ inputElement,
+ labelElement,
+ );
+
+ return (
+
+ { groupInputElement }
+ { helperTextElement }
+
+ );
+ }
+
+ const inputElement = React.cloneElement(child, injectedProps);
+
+ return (
+
+ { inputElement }
+ { labelElement }
+ { helperTextElement }
+
+ );
+ }
+
+ // Pass size value to the input component
+ const injectedProps = {
+ size: rest.size,
+ };
+ const child = React.Children.only>(children);
+ const clonedChild = React.cloneElement(child, injectedProps);
+
+ return (
+
+ { label && (
+
+ { label }
+
+
+ ) }
+ { clonedChild }
+ { helperText && (
+ { helperText }
+ ) }
+ { errorText && (
+ { errorText }
+ ) }
+
+ );
+ },
+);
diff --git a/toolkit/chakra/heading.tsx b/toolkit/chakra/heading.tsx
new file mode 100644
index 0000000000..b32af9d94f
--- /dev/null
+++ b/toolkit/chakra/heading.tsx
@@ -0,0 +1,38 @@
+import type { HeadingProps as ChakraHeadingProps } from '@chakra-ui/react';
+import { Heading as ChakraHeading } from '@chakra-ui/react';
+import React from 'react';
+
+export interface HeadingProps extends ChakraHeadingProps {
+ level?: '1' | '2' | '3';
+}
+
+export const Heading = React.forwardRef(
+ function Heading(props, ref) {
+ const { level, ...rest } = props;
+
+ const textStyle = (() => {
+ if (level === '1') {
+ return { base: 'heading.md', lg: 'heading.xl' };
+ }
+
+ if (level === '2') {
+ return { base: 'heading.sm', lg: 'heading.lg' };
+ }
+
+ return { base: 'heading.xs', lg: 'heading.md' };
+ })();
+
+ const as = (() => {
+ if (level === '1') {
+ return 'h1';
+ }
+
+ if (level === '2') {
+ return 'h2';
+ }
+
+ return 'h3';
+ })();
+
+ return ;
+ });
diff --git a/toolkit/chakra/icon-button.tsx b/toolkit/chakra/icon-button.tsx
new file mode 100644
index 0000000000..fb31b0387f
--- /dev/null
+++ b/toolkit/chakra/icon-button.tsx
@@ -0,0 +1,58 @@
+import React from 'react';
+
+import getComponentDisplayName from '../utils/getComponentDisplayName';
+import { Button, type ButtonProps } from './button';
+
+export interface IconButtonProps extends Omit {
+ size?: '2xs' | 'md';
+}
+
+export const IconButton = React.forwardRef(
+ function IconButton(props, ref) {
+ const { size, variant = 'plain', children, ...rest } = props;
+
+ // FIXME: I have to clone the children instead of using _icon props because of style overrides
+ // in some pw tests for some reason the _icon style will be applied before the style of child (IconSvg component)
+ const child = React.Children.only(children as React.ReactElement);
+ const clonedChildren = size && getComponentDisplayName(child.type) === 'IconSvg' ?
+ React.cloneElement(child, { boxSize: 5 }) :
+ child;
+
+ const sizeStyle = (() => {
+ switch (size) {
+ case '2xs': {
+ return {
+ _icon: { boxSize: 5 },
+ boxSize: 5,
+ borderRadius: 'sm',
+ };
+ }
+ case 'md': {
+ return {
+ _icon: { boxSize: 5 },
+ boxSize: 8,
+ };
+ }
+ default:
+ return {};
+ }
+ })();
+
+ return (
+
+ { clonedChildren }
+
+ );
+ },
+);
diff --git a/toolkit/chakra/image.tsx b/toolkit/chakra/image.tsx
new file mode 100644
index 0000000000..60118e7590
--- /dev/null
+++ b/toolkit/chakra/image.tsx
@@ -0,0 +1,51 @@
+import type { BoxProps, ImageProps as ChakraImageProps } from '@chakra-ui/react';
+import { Image as ChakraImage } from '@chakra-ui/react';
+import React from 'react';
+
+import { Skeleton } from './skeleton';
+
+export interface ImageProps extends ChakraImageProps {
+ fallback?: React.ReactNode;
+}
+
+export const Image = React.forwardRef(
+ function Image(props, ref) {
+ const { fallback, src, onLoad, onError, ...rest } = props;
+
+ const [ loading, setLoading ] = React.useState(true);
+ const [ error, setError ] = React.useState(false);
+
+ const handleLoadError = React.useCallback((event: React.SyntheticEvent) => {
+ setError(true);
+ setLoading(false);
+ onError?.(event);
+ }, [ onError ]);
+
+ const handleLoadSuccess = React.useCallback((event: React.SyntheticEvent) => {
+ setLoading(false);
+ onLoad?.(event);
+ }, [ onLoad ]);
+
+ if (!src && fallback) {
+ return fallback;
+ }
+
+ if (error) {
+ return fallback;
+ }
+
+ return (
+ <>
+ { loading && }
+
+ >
+ );
+ },
+);
diff --git a/toolkit/chakra/input-group.tsx b/toolkit/chakra/input-group.tsx
new file mode 100644
index 0000000000..7f493ade9e
--- /dev/null
+++ b/toolkit/chakra/input-group.tsx
@@ -0,0 +1,88 @@
+import type { BoxProps, InputElementProps } from '@chakra-ui/react';
+import { Group, InputElement } from '@chakra-ui/react';
+import { debounce } from 'es-toolkit';
+import * as React from 'react';
+
+import getComponentDisplayName from '../utils/getComponentDisplayName';
+import type { InputProps } from './input';
+
+export interface InputGroupProps extends BoxProps {
+ startElementProps?: InputElementProps;
+ endElementProps?: InputElementProps;
+ startElement?: React.ReactNode;
+ endElement?: React.ReactNode;
+ children: React.ReactElement;
+ startOffset?: InputElementProps['paddingStart'];
+ endOffset?: InputElementProps['paddingEnd'];
+}
+
+export const InputGroup = React.forwardRef(
+ function InputGroup(props, ref) {
+ const {
+ startElement,
+ startElementProps,
+ endElement,
+ endElementProps,
+ children,
+ startOffset,
+ endOffset,
+ ...rest
+ } = props;
+
+ const startElementRef = React.useRef(null);
+ const endElementRef = React.useRef(null);
+
+ const [ inlinePaddings, setInlinePaddings ] = React.useState<{ start?: number; end?: number }>();
+
+ const calculateInlinePaddings = React.useCallback(() => {
+ const { width: endWidth } = endElementRef?.current?.getBoundingClientRect() ?? {};
+ const { width: startWidth } = startElementRef?.current?.getBoundingClientRect() ?? {};
+
+ setInlinePaddings({
+ start: startWidth ?? 0,
+ end: endWidth ?? 0,
+ });
+ }, []);
+
+ React.useEffect(() => {
+ calculateInlinePaddings();
+
+ const resizeHandler = debounce(calculateInlinePaddings, 300);
+ const resizeObserver = new ResizeObserver(resizeHandler);
+ resizeObserver.observe(window.document.body);
+
+ return function cleanup() {
+ resizeObserver.unobserve(window.document.body);
+ };
+ }, [ calculateInlinePaddings ]);
+
+ return (
+
+ { startElement && (
+
+ { startElement }
+
+ ) }
+ { React.Children.map(children, (child: React.ReactElement) => {
+ if (getComponentDisplayName(child.type) !== 'FieldInput') {
+ return child;
+ }
+ return React.cloneElement(child, {
+ ...(startElement && { ps: startOffset ?? (inlinePaddings?.start ? `${ inlinePaddings.start }px` : undefined) }),
+ ...(endElement && { pe: endOffset ?? (inlinePaddings?.end ? `${ inlinePaddings.end }px` : undefined) }),
+ // hide input value and placeholder for the first render
+ value: inlinePaddings ? child.props.value : undefined,
+ placeholder: inlinePaddings ? child.props.placeholder : undefined,
+ });
+ }) }
+ { endElement && (
+
+ { endElement }
+
+ ) }
+
+ );
+ },
+);
+
+InputGroup.displayName = 'InputGroup';
diff --git a/toolkit/chakra/input.tsx b/toolkit/chakra/input.tsx
new file mode 100644
index 0000000000..5c7dad5b31
--- /dev/null
+++ b/toolkit/chakra/input.tsx
@@ -0,0 +1,8 @@
+import type { InputProps as ChakraInputProps } from '@chakra-ui/react';
+import { Input as ChakraInput } from '@chakra-ui/react';
+
+export interface InputProps extends Omit {
+ size?: 'sm' | 'md' | 'lg' | '2xl';
+}
+
+export const Input = ChakraInput;
diff --git a/toolkit/chakra/link.tsx b/toolkit/chakra/link.tsx
new file mode 100644
index 0000000000..d548faeebd
--- /dev/null
+++ b/toolkit/chakra/link.tsx
@@ -0,0 +1,114 @@
+import type { LinkProps as ChakraLinkProps } from '@chakra-ui/react';
+import { Link as ChakraLink, LinkBox as ChakraLinkBox, LinkOverlay as ChakraLinkOverlay } from '@chakra-ui/react';
+import NextLink from 'next/link';
+import type { LinkProps as NextLinkProps } from 'next/link';
+import React from 'react';
+
+import IconSvg from 'ui/shared/IconSvg';
+
+import { Skeleton } from './skeleton';
+
+export const LinkExternalIcon = ({ color }: { color?: ChakraLinkProps['color'] }) => (
+
+);
+
+interface LinkPropsChakra extends ChakraLinkProps {
+ loading?: boolean;
+ external?: boolean;
+ iconColor?: ChakraLinkProps['color'];
+ noIcon?: boolean;
+ disabled?: boolean;
+}
+
+interface LinkPropsNext extends Pick {}
+
+export interface LinkProps extends LinkPropsChakra, LinkPropsNext {}
+
+const splitProps = (props: LinkProps): { chakra: LinkPropsChakra; next: NextLinkProps } => {
+ const { scroll = true, shallow = false, prefetch = false, ...rest } = props;
+
+ return {
+ chakra: rest,
+ next: {
+ href: rest.href as NextLinkProps['href'],
+ scroll,
+ shallow,
+ prefetch,
+ },
+ };
+};
+
+export const Link = React.forwardRef(
+ function Link(props, ref) {
+ const { chakra, next } = splitProps(props);
+ const { external, loading, href, children, disabled, noIcon, iconColor, ...rest } = chakra;
+
+ if (external) {
+ return (
+ } asChild>
+
+ { children }
+ { !noIcon && }
+
+
+ );
+ }
+
+ return (
+ } asChild>
+
+ { next.href ? (
+
+ { children }
+
+ ) :
+ { children }
+ }
+
+
+ );
+ },
+);
+
+export const LinkBox = ChakraLinkBox;
+
+export const LinkOverlay = React.forwardRef(
+ function LinkOverlay(props, ref) {
+ const { chakra, next } = splitProps(props);
+ const { children, external, ...rest } = chakra;
+
+ if (external) {
+ return (
+
+ { children }
+
+ );
+ }
+
+ return (
+
+ { next.href ? { children } : children }
+
+ );
+ },
+);
diff --git a/toolkit/chakra/menu.tsx b/toolkit/chakra/menu.tsx
new file mode 100644
index 0000000000..358dca612c
--- /dev/null
+++ b/toolkit/chakra/menu.tsx
@@ -0,0 +1,124 @@
+'use client';
+
+import { AbsoluteCenter, Menu as ChakraMenu, Portal } from '@chakra-ui/react';
+import * as React from 'react';
+import { LuCheck, LuChevronRight } from 'react-icons/lu';
+
+interface MenuContentProps extends ChakraMenu.ContentProps {
+ portalled?: boolean;
+ portalRef?: React.RefObject;
+}
+
+export const MenuContent = React.forwardRef(
+ function MenuContent(props, ref) {
+ const { portalled = true, portalRef, ...rest } = props;
+ return (
+
+
+
+
+
+ );
+ },
+);
+
+export const MenuArrow = React.forwardRef<
+ HTMLDivElement,
+ ChakraMenu.ArrowProps
+>(function MenuArrow(props, ref) {
+ return (
+
+
+
+ );
+});
+
+export const MenuCheckboxItem = React.forwardRef<
+ HTMLDivElement,
+ ChakraMenu.CheckboxItemProps
+>(function MenuCheckboxItem(props, ref) {
+ return (
+
+
+
+
+
+
+ { props.children }
+
+ );
+});
+
+export const MenuRadioItem = React.forwardRef<
+ HTMLDivElement,
+ ChakraMenu.RadioItemProps
+>(function MenuRadioItem(props, ref) {
+ const { children, ...rest } = props;
+ return (
+
+
+
+
+
+
+ { children }
+
+ );
+});
+
+export const MenuItemGroup = React.forwardRef<
+ HTMLDivElement,
+ ChakraMenu.ItemGroupProps
+>(function MenuItemGroup(props, ref) {
+ const { title, children, ...rest } = props;
+ return (
+
+ { title && (
+
+ { title }
+
+ ) }
+ { children }
+
+ );
+});
+
+export interface MenuTriggerItemProps extends ChakraMenu.ItemProps {
+ startIcon?: React.ReactNode;
+}
+
+export const MenuTriggerItem = React.forwardRef<
+ HTMLDivElement,
+ MenuTriggerItemProps
+>(function MenuTriggerItem(props, ref) {
+ const { startIcon, children, ...rest } = props;
+ return (
+
+ { startIcon }
+ { children }
+
+
+ );
+});
+
+export const MenuRadioItemGroup = ChakraMenu.RadioItemGroup;
+export const MenuContextTrigger = ChakraMenu.ContextTrigger;
+export const MenuRoot = (props: ChakraMenu.RootProps) => {
+ const { lazyMount = true, unmountOnExit = true, ...rest } = props;
+ const positioning = {
+ placement: 'bottom-start' as const,
+ ...props.positioning,
+ offset: {
+ mainAxis: 4,
+ ...props.positioning?.offset,
+ },
+ };
+
+ return ;
+};
+export const MenuSeparator = ChakraMenu.Separator;
+
+export const MenuItem = ChakraMenu.Item;
+export const MenuItemText = ChakraMenu.ItemText;
+export const MenuItemCommand = ChakraMenu.ItemCommand;
+export const MenuTrigger = ChakraMenu.Trigger;
diff --git a/toolkit/chakra/pin-input.tsx b/toolkit/chakra/pin-input.tsx
new file mode 100644
index 0000000000..8f5f923939
--- /dev/null
+++ b/toolkit/chakra/pin-input.tsx
@@ -0,0 +1,27 @@
+import { PinInput as ChakraPinInput, Group } from '@chakra-ui/react';
+import * as React from 'react';
+
+export interface PinInputProps extends ChakraPinInput.RootProps {
+ rootRef?: React.Ref;
+ count?: number;
+ inputProps?: React.InputHTMLAttributes;
+ attached?: boolean;
+}
+
+export const PinInput = React.forwardRef(
+ function PinInput(props, ref) {
+ const { count = 6, inputProps, rootRef, attached, placeholder = ' ', bgColor, ...rest } = props;
+ return (
+
+
+
+
+ { Array.from({ length: count }).map((_, index) => (
+
+ )) }
+
+
+
+ );
+ },
+);
diff --git a/toolkit/chakra/popover.tsx b/toolkit/chakra/popover.tsx
new file mode 100644
index 0000000000..e79a4f8fc8
--- /dev/null
+++ b/toolkit/chakra/popover.tsx
@@ -0,0 +1,108 @@
+import { Popover as ChakraPopover, Portal } from '@chakra-ui/react';
+import * as React from 'react';
+
+import { CloseButton } from './close-button';
+
+interface PopoverContentProps extends ChakraPopover.ContentProps {
+ portalled?: boolean;
+ portalRef?: React.RefObject;
+}
+
+export const PopoverContent = React.forwardRef<
+ HTMLDivElement,
+ PopoverContentProps
+>(function PopoverContent(props, ref) {
+ const { portalled = true, portalRef, ...rest } = props;
+ return (
+
+
+
+
+
+ );
+});
+
+export const PopoverArrow = React.forwardRef<
+ HTMLDivElement,
+ ChakraPopover.ArrowProps
+>(function PopoverArrow(props, ref) {
+ return (
+
+
+
+ );
+});
+
+export const PopoverCloseTrigger = React.forwardRef<
+ HTMLButtonElement,
+ ChakraPopover.CloseTriggerProps
+>(function PopoverCloseTrigger(props, ref) {
+ return (
+
+
+
+ );
+});
+
+export const PopoverCloseTriggerWrapper = React.forwardRef<
+ HTMLButtonElement,
+ ChakraPopover.CloseTriggerProps
+>(function PopoverCloseTriggerWrapper(props, ref) {
+ const { disabled, ...rest } = props;
+
+ if (disabled) {
+ return props.children;
+ }
+
+ return (
+
+ );
+});
+
+export const PopoverRoot = (props: ChakraPopover.RootProps) => {
+ const positioning = {
+ placement: 'bottom-start' as const,
+ overflowPadding: 4,
+ ...props.positioning,
+ offset: {
+ mainAxis: 4,
+ ...props.positioning?.offset,
+ },
+ };
+ const { lazyMount = true, unmountOnExit = true, ...rest } = props;
+
+ return (
+
+ );
+};
+
+export const PopoverTrigger = React.forwardRef<
+ HTMLButtonElement,
+ ChakraPopover.TriggerProps
+>(function PopoverTrigger(props, ref) {
+ const { asChild = true, ...rest } = props;
+ return ;
+});
+
+export const PopoverTitle = ChakraPopover.Title;
+export const PopoverDescription = ChakraPopover.Description;
+export const PopoverFooter = ChakraPopover.Footer;
+export const PopoverHeader = ChakraPopover.Header;
+export const PopoverBody = ChakraPopover.Body;
diff --git a/toolkit/chakra/progress-circle.tsx b/toolkit/chakra/progress-circle.tsx
new file mode 100644
index 0000000000..1f6b31fd47
--- /dev/null
+++ b/toolkit/chakra/progress-circle.tsx
@@ -0,0 +1,38 @@
+import type { SystemStyleObject } from '@chakra-ui/react';
+import {
+ AbsoluteCenter,
+ ProgressCircle as ChakraProgressCircle,
+} from '@chakra-ui/react';
+import * as React from 'react';
+
+export interface ProgressCircleRingProps extends ChakraProgressCircle.CircleProps {
+ trackColor?: SystemStyleObject['stroke'];
+ cap?: SystemStyleObject['strokeLinecap'];
+}
+
+export const ProgressCircleRing = React.forwardRef<
+ SVGSVGElement,
+ ProgressCircleRingProps
+>(function ProgressCircleRing(props, ref) {
+ const { trackColor, cap, color, ...rest } = props;
+ return (
+
+
+
+
+ );
+});
+
+export const ProgressCircleValueText = React.forwardRef<
+ HTMLDivElement,
+ ChakraProgressCircle.ValueTextProps
+>(function ProgressCircleValueText(props, ref) {
+ return (
+
+
+
+ );
+});
+
+export interface ProgressCircleRootProps extends ChakraProgressCircle.RootProps {}
+export const ProgressCircleRoot = ChakraProgressCircle.Root;
diff --git a/toolkit/chakra/provider.tsx b/toolkit/chakra/provider.tsx
new file mode 100644
index 0000000000..637d105681
--- /dev/null
+++ b/toolkit/chakra/provider.tsx
@@ -0,0 +1,19 @@
+'use client';
+
+import { ChakraProvider } from '@chakra-ui/react';
+import React from 'react';
+
+import theme from 'toolkit/theme/theme';
+
+import {
+ ColorModeProvider,
+ type ColorModeProviderProps,
+} from './color-mode';
+
+export function Provider(props: ColorModeProviderProps) {
+ return (
+
+
+
+ );
+}
diff --git a/toolkit/chakra/radio.tsx b/toolkit/chakra/radio.tsx
new file mode 100644
index 0000000000..832f75f9fb
--- /dev/null
+++ b/toolkit/chakra/radio.tsx
@@ -0,0 +1,26 @@
+import { RadioGroup as ChakraRadioGroup } from '@chakra-ui/react';
+import * as React from 'react';
+
+export interface RadioProps extends ChakraRadioGroup.ItemProps {
+ rootRef?: React.Ref;
+ inputProps?: React.InputHTMLAttributes;
+}
+
+export const Radio = React.forwardRef(
+ function Radio(props, ref) {
+ const { children, inputProps, rootRef, ...rest } = props;
+ return (
+
+
+
+ { children && (
+ { children }
+ ) }
+
+ );
+ },
+);
+
+export interface RadioGroupProps extends ChakraRadioGroup.RootProps {}
+
+export const RadioGroup = ChakraRadioGroup.Root;
diff --git a/toolkit/chakra/rating.tsx b/toolkit/chakra/rating.tsx
new file mode 100644
index 0000000000..3895192f93
--- /dev/null
+++ b/toolkit/chakra/rating.tsx
@@ -0,0 +1,40 @@
+import { RatingGroup, useRatingGroup } from '@chakra-ui/react';
+import * as React from 'react';
+
+import IconSvg from 'ui/shared/IconSvg';
+
+export interface RatingProps extends Omit {
+ count?: number;
+ label?: string | Array;
+ defaultValue?: number;
+ onValueChange?: ({ value }: { value: number }) => void;
+ readOnly?: boolean;
+}
+
+export const Rating = React.forwardRef(
+ function Rating(props, ref) {
+ const { count = 5, label: labelProp, defaultValue, onValueChange, readOnly, ...rest } = props;
+ const store = useRatingGroup({ count, defaultValue, onValueChange, readOnly });
+
+ const highlightedIndex = store.hovering && !readOnly ? store.hoveredValue : store.value;
+ const label = Array.isArray(labelProp) ? labelProp[highlightedIndex - 1] : labelProp;
+
+ return (
+
+
+
+ { Array.from({ length: count }).map((_, index) => {
+ const icon = index < highlightedIndex ? : ;
+
+ return (
+
+
+
+ );
+ }) }
+
+ { label && { label } }
+
+ );
+ },
+);
diff --git a/toolkit/chakra/select.tsx b/toolkit/chakra/select.tsx
new file mode 100644
index 0000000000..83d46745c2
--- /dev/null
+++ b/toolkit/chakra/select.tsx
@@ -0,0 +1,341 @@
+'use client';
+
+import type { ListCollection } from '@chakra-ui/react';
+import { Box, Select as ChakraSelect, createListCollection, Flex, Portal, useSelectContext } from '@chakra-ui/react';
+import { useDebounce } from '@uidotdev/usehooks';
+import * as React from 'react';
+
+import FilterInput from 'ui/shared/filters/FilterInput';
+import type { IconName } from 'ui/shared/IconSvg';
+import IconSvg from 'ui/shared/IconSvg';
+
+import { CloseButton } from './close-button';
+import { Skeleton } from './skeleton';
+
+export interface SelectOption {
+ value: Value;
+ label: string;
+ icon?: IconName | React.ReactNode;
+}
+
+export interface SelectControlProps extends ChakraSelect.ControlProps {
+ noIndicator?: boolean;
+ triggerProps?: ChakraSelect.TriggerProps;
+ loading?: boolean;
+}
+
+export const SelectControl = React.forwardRef<
+ HTMLButtonElement,
+ SelectControlProps
+>(function SelectControl(props, ref) {
+ // NOTE: here defaultValue means the "default" option of the select, not its initial value
+ const { children, noIndicator, triggerProps, loading, defaultValue, ...rest } = props;
+
+ const context = useSelectContext();
+ const isDefaultValue = Array.isArray(defaultValue) ? context.value.every((item) => defaultValue.includes(item)) : context.value === defaultValue;
+
+ return (
+
+
+ { children }
+ { (!noIndicator) && (
+
+ { !noIndicator && (
+
+
+
+ ) }
+
+ ) }
+
+
+ );
+});
+
+export const SelectClearTrigger = React.forwardRef<
+ HTMLButtonElement,
+ ChakraSelect.ClearTriggerProps
+>(function SelectClearTrigger(props, ref) {
+ return (
+
+
+
+ );
+});
+
+interface SelectContentProps extends ChakraSelect.ContentProps {
+ portalled?: boolean;
+ portalRef?: React.RefObject;
+}
+
+export const SelectContent = React.forwardRef<
+ HTMLDivElement,
+ SelectContentProps
+>(function SelectContent(props, ref) {
+ const { portalled = true, portalRef, ...rest } = props;
+ return (
+
+
+
+
+
+ );
+});
+
+export const SelectItem = React.forwardRef<
+ HTMLDivElement,
+ ChakraSelect.ItemProps
+>(function SelectItem(props, ref) {
+ const { item, children, ...rest } = props;
+
+ const startElement = (() => {
+ if (item.icon) {
+ return typeof item.icon === 'string' ? : item.icon;
+ }
+
+ return null;
+ })();
+
+ return (
+
+ { startElement }
+ { children }
+
+
+
+
+ );
+});
+
+interface SelectValueTextProps extends Omit {
+ children?(items: Array): React.ReactNode;
+ size?: SelectRootProps['size'];
+ required?: boolean;
+ invalid?: boolean;
+ errorText?: string;
+}
+
+export const SelectValueText = React.forwardRef<
+ HTMLSpanElement,
+ SelectValueTextProps
+>(function SelectValueText(props, ref) {
+ const { children, size, required, invalid, errorText, ...rest } = props;
+ const context = useSelectContext();
+
+ const content = (() => {
+ const items = context.selectedItems;
+
+ const placeholder = `${ props.placeholder }${ required ? '*' : '' }${ invalid && errorText ? ` - ${ errorText }` : '' }`;
+
+ if (items.length === 0) return placeholder;
+
+ if (children) return children(items);
+
+ if (items.length === 1) {
+ const item = items[0] as SelectOption;
+
+ if (!item) return placeholder;
+
+ const icon = (() => {
+ if (item.icon) {
+ return typeof item.icon === 'string' ? : item.icon;
+ }
+
+ return null;
+ })();
+
+ const label = size === 'lg' ? (
+
+ { placeholder }
+
+ ) : null;
+
+ return (
+ <>
+ { label }
+
+ { icon }
+
+ { context.collection.stringifyItem(item) }
+
+
+ >
+ );
+ }
+
+ // FIXME: we don't have multiple selection in the select yet
+ return `${ items.length } selected`;
+ })();
+
+ return (
+
+ { content }
+
+ );
+});
+
+export interface SelectRootProps extends ChakraSelect.RootProps {}
+
+export const SelectRoot = React.forwardRef<
+ HTMLDivElement,
+ ChakraSelect.RootProps
+>(function SelectRoot(props, ref) {
+ const { lazyMount = true, unmountOnExit = true, ...rest } = props;
+ return (
+
+ { props.asChild ? (
+ props.children
+ ) : (
+ <>
+
+ { props.children }
+ >
+ ) }
+
+ );
+}) as ChakraSelect.RootComponent;
+
+interface SelectItemGroupProps extends ChakraSelect.ItemGroupProps {
+ label: React.ReactNode;
+}
+
+export const SelectItemGroup = React.forwardRef<
+ HTMLDivElement,
+ SelectItemGroupProps
+>(function SelectItemGroup(props, ref) {
+ const { children, label, ...rest } = props;
+ return (
+
+ { label }
+ { children }
+
+ );
+});
+
+export const SelectLabel = ChakraSelect.Label;
+export const SelectItemText = ChakraSelect.ItemText;
+
+export interface SelectProps extends SelectRootProps {
+ collection: ListCollection;
+ placeholder: string;
+ portalled?: boolean;
+ loading?: boolean;
+ errorText?: string;
+}
+
+export const Select = React.forwardRef((props, ref) => {
+ const { collection, placeholder, portalled = true, loading, errorText, ...rest } = props;
+ return (
+
+
+
+
+
+ { collection.items.map((item: SelectOption) => (
+
+ { item.label }
+
+ )) }
+
+
+ );
+});
+
+export interface SelectAsyncProps extends Omit {
+ placeholder: string;
+ portalled?: boolean;
+ loading?: boolean;
+ loadOptions: (input: string, currentValue: Array) => Promise>;
+ extraControls?: React.ReactNode;
+}
+
+export const SelectAsync = React.forwardRef((props, ref) => {
+ const { placeholder, portalled = true, loading, loadOptions, extraControls, onValueChange, errorText, ...rest } = props;
+
+ const [ collection, setCollection ] = React.useState>(createListCollection({ items: [] }));
+ const [ inputValue, setInputValue ] = React.useState('');
+ const [ value, setValue ] = React.useState>([]);
+
+ const debouncedInputValue = useDebounce(inputValue, 300);
+
+ React.useEffect(() => {
+ loadOptions(debouncedInputValue, value).then(setCollection);
+ }, [ debouncedInputValue, loadOptions, value ]);
+
+ const handleFilterChange = React.useCallback((value: string) => {
+ setInputValue(value);
+ }, [ ]);
+
+ const handleValueChange = React.useCallback(({ value, items }: { value: Array; items: Array }) => {
+ setValue(value);
+ onValueChange?.({ value, items });
+ }, [ onValueChange ]);
+
+ return (
+
+
+
+
+
+
+
+ { extraControls }
+
+ { collection.items.map((item) => (
+
+ { item.label }
+
+ )) }
+
+
+ );
+});
diff --git a/toolkit/chakra/skeleton.tsx b/toolkit/chakra/skeleton.tsx
new file mode 100644
index 0000000000..b4da4d8f0a
--- /dev/null
+++ b/toolkit/chakra/skeleton.tsx
@@ -0,0 +1,62 @@
+import type {
+ SkeletonProps as ChakraSkeletonProps,
+ CircleProps,
+} from '@chakra-ui/react';
+import { Skeleton as ChakraSkeleton, Circle, Stack } from '@chakra-ui/react';
+import * as React from 'react';
+
+export interface SkeletonCircleProps extends ChakraSkeletonProps {
+ size?: CircleProps['size'];
+}
+
+export const SkeletonCircle = React.forwardRef<
+ HTMLDivElement,
+ SkeletonCircleProps
+>(function SkeletonCircle(props, ref) {
+ const { size, ...rest } = props;
+ return (
+
+
+
+ );
+});
+
+export interface SkeletonTextProps extends ChakraSkeletonProps {
+ noOfLines?: number;
+}
+
+export const SkeletonText = React.forwardRef(
+ function SkeletonText(props, ref) {
+ const { noOfLines = 3, gap, ...rest } = props;
+ return (
+
+ { Array.from({ length: noOfLines }).map((_, index) => (
+
+ )) }
+
+ );
+ },
+);
+
+export interface SkeletonProps extends Omit {
+ loading: boolean | undefined;
+}
+
+export const Skeleton = React.forwardRef(
+ function Skeleton(props, ref) {
+ const { loading = false, ...rest } = props;
+ return (
+
+ );
+ },
+);
diff --git a/toolkit/chakra/slider.tsx b/toolkit/chakra/slider.tsx
new file mode 100644
index 0000000000..5e1fddca22
--- /dev/null
+++ b/toolkit/chakra/slider.tsx
@@ -0,0 +1,82 @@
+import { Slider as ChakraSlider, For, HStack } from '@chakra-ui/react';
+import * as React from 'react';
+
+export interface SliderProps extends ChakraSlider.RootProps {
+ marks?: Array;
+ label?: React.ReactNode;
+ showValue?: boolean;
+}
+
+export const Slider = React.forwardRef(
+ function Slider(props, ref) {
+ const { marks: marksProp, label, showValue, ...rest } = props;
+ const value = props.defaultValue ?? props.value;
+
+ const marks = marksProp?.map((mark) => {
+ if (typeof mark === 'number') return { value: mark, label: undefined };
+ return mark;
+ });
+
+ const hasMarkLabel = Boolean(marks?.some((mark) => mark.label));
+
+ return (
+
+ { label && !showValue && (
+ { label }
+ ) }
+ { label && showValue && (
+
+ { label }
+
+
+ ) }
+
+
+
+
+
+
+
+
+ );
+ },
+);
+
+function SliderThumbs(props: { value?: Array }) {
+ const { value } = props;
+ return (
+
+ { (_, index) => (
+
+
+
+ ) }
+
+ );
+}
+
+interface SliderMarksProps {
+ marks?: Array;
+}
+
+const SliderMarks = React.forwardRef(
+ function SliderMarks(props, ref) {
+ const { marks } = props;
+ if (!marks?.length) return null;
+
+ return (
+
+ { marks.map((mark, index) => {
+ const value = typeof mark === 'number' ? mark : mark.value;
+ const label = typeof mark === 'number' ? undefined : mark.label;
+ return (
+
+
+ { label }
+
+ );
+ }) }
+
+ );
+ },
+);
diff --git a/toolkit/chakra/switch.tsx b/toolkit/chakra/switch.tsx
new file mode 100644
index 0000000000..ea48c54bb5
--- /dev/null
+++ b/toolkit/chakra/switch.tsx
@@ -0,0 +1,40 @@
+import { Switch as ChakraSwitch } from '@chakra-ui/react';
+import * as React from 'react';
+
+export interface SwitchProps extends ChakraSwitch.RootProps {
+ inputProps?: React.InputHTMLAttributes;
+ labelProps?: ChakraSwitch.LabelProps;
+ rootRef?: React.Ref;
+ trackLabel?: { on: React.ReactNode; off: React.ReactNode };
+ thumbLabel?: { on: React.ReactNode; off: React.ReactNode };
+}
+
+export const Switch = React.forwardRef(
+ function Switch(props, ref) {
+ const { inputProps, children, rootRef, trackLabel, thumbLabel, labelProps, ...rest } =
+ props;
+
+ return (
+
+
+
+
+ { thumbLabel && (
+
+ { thumbLabel?.on }
+
+ ) }
+
+ { trackLabel && (
+
+ { trackLabel.on }
+
+ ) }
+
+ { children != null && (
+ { children }
+ ) }
+
+ );
+ },
+);
diff --git a/toolkit/chakra/table.tsx b/toolkit/chakra/table.tsx
new file mode 100644
index 0000000000..c970792347
--- /dev/null
+++ b/toolkit/chakra/table.tsx
@@ -0,0 +1,111 @@
+import { Table as ChakraTable } from '@chakra-ui/react';
+import { throttle } from 'es-toolkit';
+import * as React from 'react';
+
+import IconSvg from 'ui/shared/IconSvg';
+
+import { Link } from './link';
+
+export const TableRoot = ChakraTable.Root;
+export const TableBody = ChakraTable.Body;
+export const TableHeader = ChakraTable.Header;
+export const TableRow = ChakraTable.Row;
+
+export interface TableCellProps extends ChakraTable.CellProps {
+ isNumeric?: boolean;
+}
+
+export const TableCell = (props: TableCellProps) => {
+ const { isNumeric, ...rest } = props;
+
+ return ;
+};
+
+export interface TableColumnHeaderProps extends ChakraTable.ColumnHeaderProps {
+ isNumeric?: boolean;
+}
+
+export const TableColumnHeader = (props: TableColumnHeaderProps) => {
+ const { isNumeric, ...rest } = props;
+
+ return ;
+};
+
+export interface TableColumnHeaderSortableProps extends TableColumnHeaderProps {
+ sortField: F;
+ sortValue: string;
+ onSortToggle: (sortField: F) => void;
+ disabled?: boolean;
+ indicatorPosition?: 'left' | 'right';
+}
+
+export const TableColumnHeaderSortable = (props: TableColumnHeaderSortableProps) => {
+ const { sortField, sortValue, onSortToggle, children, disabled, indicatorPosition = 'left', ...rest } = props;
+
+ const handleSortToggle = React.useCallback(() => {
+ onSortToggle(sortField);
+ }, [ onSortToggle, sortField ]);
+
+ return (
+
+
+ { sortValue.includes(sortField) && (
+
+ ) }
+ { children }
+
+
+ );
+};
+
+export interface TableHeaderProps extends ChakraTable.HeaderProps {
+ top?: number;
+}
+
+export const TableHeaderSticky = (props: TableHeaderProps) => {
+ const { top, children, ...rest } = props;
+
+ const ref = React.useRef(null);
+ const [ isStuck, setIsStuck ] = React.useState(false);
+
+ const handleScroll = React.useCallback(() => {
+ if (Number(ref.current?.getBoundingClientRect().y) <= (top || 0)) {
+ setIsStuck(true);
+ } else {
+ setIsStuck(false);
+ }
+ }, [ top ]);
+
+ React.useEffect(() => {
+ const throttledHandleScroll = throttle(handleScroll, 300);
+
+ window.addEventListener('scroll', throttledHandleScroll);
+
+ return () => {
+ window.removeEventListener('scroll', throttledHandleScroll);
+ };
+ }, [ handleScroll ]);
+
+ return (
+
+ { children }
+
+ );
+};
diff --git a/toolkit/chakra/tabs.tsx b/toolkit/chakra/tabs.tsx
new file mode 100644
index 0000000000..905057b3c5
--- /dev/null
+++ b/toolkit/chakra/tabs.tsx
@@ -0,0 +1,46 @@
+import { Tabs as ChakraTabs, chakra } from '@chakra-ui/react';
+import * as React from 'react';
+
+export interface TabsProps extends ChakraTabs.RootProps {}
+
+export const TabsRoot = React.forwardRef(
+ function TabsRoot(props, ref) {
+ const { lazyMount = true, unmountOnExit = true, ...rest } = props;
+ return ;
+ },
+);
+
+export const TabsList = ChakraTabs.List;
+
+export interface TabsTriggerProps extends ChakraTabs.TriggerProps {}
+
+export const TabsTrigger = React.forwardRef(
+ function TabsTrigger(props, ref) {
+ return ;
+ },
+);
+
+export const TabsContent = ChakraTabs.Content;
+
+export interface TabsCounterProps {
+ count?: number | null;
+}
+
+export const TabsCounter = ({ count }: TabsCounterProps) => {
+ const COUNTER_OVERLOAD = 50;
+
+ if (count === undefined || count === null) {
+ return null;
+ }
+
+ return (
+ 0 ? 'text.secondary' : { _light: 'blackAlpha.400', _dark: 'whiteAlpha.400' } }
+ _groupHover={{
+ color: 'inherit',
+ }}
+ >
+ { count > COUNTER_OVERLOAD ? `${ COUNTER_OVERLOAD }+` : count }
+
+ );
+};
diff --git a/toolkit/chakra/tag.tsx b/toolkit/chakra/tag.tsx
new file mode 100644
index 0000000000..cbe82ae7c4
--- /dev/null
+++ b/toolkit/chakra/tag.tsx
@@ -0,0 +1,73 @@
+import { chakra, Tag as ChakraTag } from '@chakra-ui/react';
+import * as React from 'react';
+
+import { nbsp } from 'lib/html-entities';
+import TruncatedTextTooltip from 'ui/shared/TruncatedTextTooltip';
+
+import { CloseButton } from './close-button';
+import { Skeleton } from './skeleton';
+
+export interface TagProps extends ChakraTag.RootProps {
+ startElement?: React.ReactNode;
+ endElement?: React.ReactNode;
+ endElementProps?: ChakraTag.EndElementProps;
+ label?: string;
+ onClose?: VoidFunction;
+ closable?: boolean;
+ truncated?: boolean;
+ loading?: boolean;
+ selected?: boolean;
+}
+
+export const Tag = React.forwardRef(
+ function Tag(props, ref) {
+ const {
+ startElement,
+ endElement,
+ endElementProps,
+ label,
+ onClose,
+ closable = Boolean(onClose),
+ children,
+ truncated = false,
+ loading,
+ selected,
+ ...rest
+ } = props;
+
+ const labelElement = label ? (
+ { label }:{ nbsp }
+ ) : null;
+
+ const contentElement = truncated ? (
+
+ { labelElement }{ children }
+
+ ) : { labelElement }{ children } ;
+
+ return (
+
+
+ { startElement && (
+ { startElement }
+ ) }
+ { contentElement }
+ { endElement && (
+ { endElement }
+ ) }
+ { closable && (
+
+
+
+
+
+ ) }
+
+
+ );
+ },
+);
diff --git a/toolkit/chakra/textarea.tsx b/toolkit/chakra/textarea.tsx
new file mode 100644
index 0000000000..f870da4b2a
--- /dev/null
+++ b/toolkit/chakra/textarea.tsx
@@ -0,0 +1,6 @@
+import type { TextareaProps as ChakraTextareaProps } from '@chakra-ui/react';
+import { Textarea as ChakraTextarea } from '@chakra-ui/react';
+
+export interface TextareaProps extends ChakraTextareaProps {}
+
+export const Textarea = ChakraTextarea;
diff --git a/toolkit/chakra/toaster.tsx b/toolkit/chakra/toaster.tsx
new file mode 100644
index 0000000000..aebe75b6ef
--- /dev/null
+++ b/toolkit/chakra/toaster.tsx
@@ -0,0 +1,60 @@
+'use client';
+
+import {
+ Toaster as ChakraToaster,
+ Portal,
+ Spinner,
+ Stack,
+ Toast,
+ createToaster,
+} from '@chakra-ui/react';
+
+import { SECOND } from 'lib/consts';
+
+import { CloseButton } from './close-button';
+
+export const toaster = createToaster({
+ placement: 'top-end',
+ pauseOnPageIdle: true,
+ duration: 10 * SECOND,
+ offsets: {
+ top: '12px',
+ right: '12px',
+ bottom: '12px',
+ left: '12px',
+ },
+});
+
+export const Toaster = () => {
+ return (
+
+
+ { (toast) => {
+ const closable = toast.meta?.closable !== undefined ? toast.meta.closable : true;
+
+ return (
+
+ { toast.type === 'loading' ? (
+
+ ) : null }
+
+ { toast.title && { toast.title } }
+ { toast.description && (
+ { toast.description }
+ ) }
+
+ { toast.action && (
+ { toast.action.label }
+ ) }
+ { closable && (
+
+
+
+ ) }
+
+ );
+ } }
+
+
+ );
+};
diff --git a/toolkit/chakra/tooltip.tsx b/toolkit/chakra/tooltip.tsx
new file mode 100644
index 0000000000..e74461004f
--- /dev/null
+++ b/toolkit/chakra/tooltip.tsx
@@ -0,0 +1,112 @@
+import { Tooltip as ChakraTooltip, Portal } from '@chakra-ui/react';
+import { useClickAway } from '@uidotdev/usehooks';
+import * as React from 'react';
+
+import config from 'configs/app';
+import useIsMobile from 'lib/hooks/useIsMobile';
+
+export interface TooltipProps extends ChakraTooltip.RootProps {
+ selected?: boolean;
+ showArrow?: boolean;
+ portalled?: boolean;
+ portalRef?: React.RefObject;
+ content: React.ReactNode;
+ contentProps?: ChakraTooltip.ContentProps;
+ triggerProps?: ChakraTooltip.TriggerProps;
+ disabled?: boolean;
+ disableOnMobile?: boolean;
+}
+
+export const Tooltip = React.forwardRef(
+ function Tooltip(props, ref) {
+ const {
+ showArrow: showArrowProp,
+ onOpenChange,
+ variant,
+ selected,
+ children,
+ disabled,
+ disableOnMobile,
+ portalled = true,
+ content,
+ contentProps,
+ portalRef,
+ defaultOpen = false,
+ lazyMount = true,
+ unmountOnExit = true,
+ triggerProps,
+ ...rest
+ } = props;
+
+ const [ open, setOpen ] = React.useState(defaultOpen);
+
+ const isMobile = useIsMobile();
+ const triggerRef = useClickAway(() => setOpen(false));
+
+ const handleOpenChange = React.useCallback((details: { open: boolean }) => {
+ setOpen(details.open);
+ onOpenChange?.(details);
+ }, [ onOpenChange ]);
+
+ const handleTriggerClick = React.useCallback(() => {
+ setOpen((prev) => !prev);
+ }, [ ]);
+
+ if (disabled || (disableOnMobile && isMobile)) return children;
+
+ const defaultShowArrow = variant === 'popover' ? false : true;
+ const showArrow = showArrowProp !== undefined ? showArrowProp : defaultShowArrow;
+
+ const positioning = {
+ ...rest.positioning,
+ overflowPadding: 4,
+ offset: {
+ mainAxis: 4,
+ ...rest.positioning?.offset,
+ },
+ };
+
+ return (
+
+
+ { children }
+
+
+
+
+ { showArrow && (
+
+
+
+ ) }
+ { content }
+
+
+
+
+ );
+ },
+);
diff --git a/toolkit/components/AdaptiveTabs/AdaptiveTabs.tsx b/toolkit/components/AdaptiveTabs/AdaptiveTabs.tsx
new file mode 100644
index 0000000000..634f0c6e42
--- /dev/null
+++ b/toolkit/components/AdaptiveTabs/AdaptiveTabs.tsx
@@ -0,0 +1,88 @@
+import React from 'react';
+
+import type { TabsProps } from 'toolkit/chakra/tabs';
+import { TabsContent, TabsRoot } from 'toolkit/chakra/tabs';
+import useViewportSize from 'toolkit/hooks/useViewportSize';
+
+import AdaptiveTabsList, { type BaseProps as AdaptiveTabsListProps } from './AdaptiveTabsList';
+import { getTabValue } from './utils';
+
+export interface Props extends TabsProps, AdaptiveTabsListProps { }
+
+const AdaptiveTabs = (props: Props) => {
+ const {
+ tabs,
+ onValueChange,
+ defaultValue,
+ isLoading,
+ listProps,
+ rightSlot,
+ rightSlotProps,
+ leftSlot,
+ leftSlotProps,
+ stickyEnabled,
+ size,
+ variant,
+ ...rest
+ } = props;
+
+ const [ activeTab, setActiveTab ] = React.useState(defaultValue || getTabValue(tabs[0]));
+
+ const handleTabChange = React.useCallback(({ value }: { value: string }) => {
+ if (isLoading) {
+ return;
+ }
+ onValueChange ? onValueChange({ value }) : setActiveTab(value);
+ }, [ isLoading, onValueChange ]);
+
+ const viewportSize = useViewportSize();
+
+ React.useEffect(() => {
+ if (defaultValue) {
+ setActiveTab(defaultValue);
+ }
+ }, [ defaultValue ]);
+
+ if (tabs.length === 1) {
+ return { tabs[0].component }
;
+ }
+
+ return (
+
+ tab.id).join(':') }
+ tabs={ tabs }
+ listProps={ listProps }
+ leftSlot={ leftSlot }
+ leftSlotProps={ leftSlotProps }
+ rightSlot={ rightSlot }
+ rightSlotProps={ rightSlotProps }
+ stickyEnabled={ stickyEnabled }
+ activeTab={ activeTab }
+ isLoading={ isLoading }
+ />
+ { tabs.map((tab) => {
+ const value = getTabValue(tab);
+ return (
+
+ { tab.component }
+
+ );
+ }) }
+
+ );
+};
+
+export default React.memo(AdaptiveTabs);
diff --git a/toolkit/components/AdaptiveTabs/AdaptiveTabsList.tsx b/toolkit/components/AdaptiveTabs/AdaptiveTabsList.tsx
new file mode 100644
index 0000000000..27a2106d0b
--- /dev/null
+++ b/toolkit/components/AdaptiveTabs/AdaptiveTabsList.tsx
@@ -0,0 +1,198 @@
+import type { HTMLChakraProps } from '@chakra-ui/react';
+import { Box } from '@chakra-ui/react';
+import React from 'react';
+
+import type { TabItemRegular } from './types';
+
+import { useScrollDirection } from 'lib/contexts/scrollDirection';
+import useIsMobile from 'lib/hooks/useIsMobile';
+import useIsSticky from 'lib/hooks/useIsSticky';
+import { Skeleton } from 'toolkit/chakra/skeleton';
+import { TabsCounter, TabsList, TabsTrigger } from 'toolkit/chakra/tabs';
+
+import AdaptiveTabsMenu from './AdaptiveTabsMenu';
+import useAdaptiveTabs from './useAdaptiveTabs';
+import useScrollToActiveTab from './useScrollToActiveTab';
+import { menuButton, getTabValue } from './utils';
+
+export interface SlotProps extends HTMLChakraProps<'div'> {
+ widthAllocation?: 'available' | 'fixed';
+}
+
+export interface BaseProps {
+ tabs: Array;
+ listProps?: HTMLChakraProps<'div'> | (({ isSticky, activeTab }: { isSticky: boolean; activeTab: string }) => HTMLChakraProps<'div'>);
+ rightSlot?: React.ReactNode;
+ rightSlotProps?: SlotProps;
+ leftSlot?: React.ReactNode;
+ leftSlotProps?: SlotProps;
+ stickyEnabled?: boolean;
+ isLoading?: boolean;
+}
+
+interface Props extends BaseProps {
+ activeTab: string;
+}
+
+const HIDDEN_ITEM_STYLES: HTMLChakraProps<'button'> = {
+ position: 'absolute',
+ top: '-9999px',
+ left: '-9999px',
+ visibility: 'hidden',
+};
+
+const getItemStyles = (index: number, tabsCut: number | undefined) => {
+ if (tabsCut === undefined) {
+ return HIDDEN_ITEM_STYLES as never;
+ }
+
+ return index < tabsCut ? {} : HIDDEN_ITEM_STYLES as never;
+};
+
+const getMenuStyles = (tabsLength: number, tabsCut: number | undefined) => {
+ if (tabsCut === undefined) {
+ return {
+ opacity: 0,
+ };
+ }
+
+ return tabsCut >= tabsLength ? HIDDEN_ITEM_STYLES : {};
+};
+
+const AdaptiveTabsList = (props: Props) => {
+
+ const {
+ tabs,
+ activeTab,
+ listProps,
+ rightSlot,
+ rightSlotProps,
+ leftSlot,
+ leftSlotProps,
+ stickyEnabled,
+ isLoading,
+ } = props;
+
+ const scrollDirection = useScrollDirection();
+ const isMobile = useIsMobile();
+
+ const tabsList = React.useMemo(() => {
+ return [ ...tabs, menuButton ];
+ }, [ tabs ]);
+
+ const { tabsCut, tabsRefs, listRef, rightSlotRef, leftSlotRef } = useAdaptiveTabs(tabsList, isMobile);
+ const isSticky = useIsSticky(listRef, 5, stickyEnabled);
+ const activeTabIndex = tabsList.findIndex((tab) => getTabValue(tab) === activeTab) ?? 0;
+ useScrollToActiveTab({ activeTabIndex, listRef, tabsRefs, isMobile, isLoading });
+
+ return (
+
+ { leftSlot && (
+
+ { leftSlot }
+
+ )
+ }
+ { tabsList.slice(0, isLoading ? 5 : Infinity).map((tab, index) => {
+ const value = getTabValue(tab);
+ const ref = tabsRefs[index];
+
+ if (tab.id === 'menu') {
+ if (isLoading) {
+ return null;
+ }
+
+ return (
+ 0 && tabsCut !== undefined && tabsCut > 0 && activeTabIndex >= tabsCut }
+ { ...getMenuStyles(tabs.length, tabsCut) }
+ />
+ );
+ }
+
+ return (
+
+ { isLoading ? (
+
+ { typeof tab.title === 'function' ? tab.title() : tab.title }
+
+
+ ) : (
+ <>
+ { typeof tab.title === 'function' ? tab.title() : tab.title }
+
+ >
+ ) }
+
+ );
+ }) }
+ {
+ rightSlot ? (
+
+ { rightSlot }
+
+ ) :
+ null
+ }
+
+ );
+};
+
+export default React.memo(AdaptiveTabsList);
diff --git a/toolkit/components/AdaptiveTabs/AdaptiveTabsMenu.tsx b/toolkit/components/AdaptiveTabs/AdaptiveTabsMenu.tsx
new file mode 100644
index 0000000000..7675a8d07a
--- /dev/null
+++ b/toolkit/components/AdaptiveTabs/AdaptiveTabsMenu.tsx
@@ -0,0 +1,75 @@
+import React from 'react';
+
+import type { TabItem } from './types';
+
+import { PopoverBody, PopoverCloseTriggerWrapper, PopoverContent, PopoverRoot, PopoverTrigger } from 'toolkit/chakra/popover';
+import { TabsCounter, TabsTrigger } from 'toolkit/chakra/tabs';
+import IconSvg from 'ui/shared/IconSvg';
+
+import { IconButton } from '../../chakra/icon-button';
+import type { IconButtonProps } from '../../chakra/icon-button';
+import { getTabValue } from './utils';
+
+interface Props extends IconButtonProps {
+ tabs: Array;
+ tabsCut: number;
+ isActive: boolean;
+}
+
+const AdaptiveTabsMenu = ({ tabs, tabsCut, isActive, ...props }: Props, ref: React.Ref) => {
+
+ return (
+
+
+
+
+
+
+
+
+ { tabs.slice(tabsCut).map((tab) => {
+ const value = getTabValue(tab);
+
+ return (
+
+
+ { typeof tab.title === 'function' ? tab.title() : tab.title }
+
+
+
+ );
+ }) }
+
+
+
+ );
+};
+
+export default React.memo(React.forwardRef(AdaptiveTabsMenu));
diff --git a/ui/shared/Tabs/types.ts b/toolkit/components/AdaptiveTabs/types.ts
similarity index 64%
rename from ui/shared/Tabs/types.ts
rename to toolkit/components/AdaptiveTabs/types.ts
index f15bb79e6f..bc3132ad24 100644
--- a/ui/shared/Tabs/types.ts
+++ b/toolkit/components/AdaptiveTabs/types.ts
@@ -1,21 +1,22 @@
import type React from 'react';
-export interface TabItem {
+export interface TabItemRegular {
// NOTE, in case of array of ids, when switching tabs, the first id will be used
// switching between other ids should be handled in the underlying component
id: string | Array;
title: string | (() => React.ReactNode);
count?: number | null;
component: React.ReactNode;
+ subTabs?: Array;
}
-export type RoutedTab = TabItem & { subTabs?: Array };
-
-export type RoutedSubTab = Omit;
-
-export interface MenuButton {
- id: null;
+export interface TabItemMenu {
+ id: 'menu';
title: string;
count?: never;
component: null;
}
+
+export type TabItem = TabItemRegular | TabItemMenu;
+
+export type SubTabItem = Omit;
diff --git a/ui/shared/Tabs/useAdaptiveTabs.tsx b/toolkit/components/AdaptiveTabs/useAdaptiveTabs.tsx
similarity index 87%
rename from ui/shared/Tabs/useAdaptiveTabs.tsx
rename to toolkit/components/AdaptiveTabs/useAdaptiveTabs.tsx
index a8c944a39e..bf3be30f82 100644
--- a/ui/shared/Tabs/useAdaptiveTabs.tsx
+++ b/toolkit/components/AdaptiveTabs/useAdaptiveTabs.tsx
@@ -1,11 +1,11 @@
import React from 'react';
-import type { MenuButton, RoutedTab } from './types';
+import type { TabItem } from './types';
-export default function useAdaptiveTabs(tabs: Array, disabled?: boolean) {
- // to avoid flickering we set initial value to 0
+export default function useAdaptiveTabs(tabs: Array, disabled?: boolean) {
+ // to avoid flickering we set initial value to undefined
// so there will be no displayed tabs initially
- const [ tabsCut, setTabsCut ] = React.useState(disabled ? tabs.length : 0);
+ const [ tabsCut, setTabsCut ] = React.useState(disabled ? tabs.length : undefined);
const [ tabsRefs, setTabsRefs ] = React.useState>>([]);
const listRef = React.useRef(null);
const rightSlotRef = React.useRef(null);
@@ -52,7 +52,7 @@ export default function useAdaptiveTabs(tabs: Array, dis
React.useEffect(() => {
setTabsRefs(tabs.map((_, index) => tabsRefs[index] || React.createRef()));
- setTabsCut(disabled ? tabs.length : 0);
+ setTabsCut(disabled ? tabs.length : undefined);
// update refs only when disabled prop changes
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [ disabled ]);
diff --git a/ui/shared/Tabs/useScrollToActiveTab.tsx b/toolkit/components/AdaptiveTabs/useScrollToActiveTab.tsx
similarity index 96%
rename from ui/shared/Tabs/useScrollToActiveTab.tsx
rename to toolkit/components/AdaptiveTabs/useScrollToActiveTab.tsx
index 8d729899d1..777aece430 100644
--- a/ui/shared/Tabs/useScrollToActiveTab.tsx
+++ b/toolkit/components/AdaptiveTabs/useScrollToActiveTab.tsx
@@ -30,6 +30,7 @@ export default function useScrollToActiveTab({ activeTabIndex, tabsRefs, listRef
const isWithinFirstPage = containerWidth > left + activeTabWidth;
if (isWithinFirstPage) {
+ listRef.current.scrollTo({ left: 0 });
return;
}
diff --git a/toolkit/components/AdaptiveTabs/utils.ts b/toolkit/components/AdaptiveTabs/utils.ts
new file mode 100644
index 0000000000..cae9be4bbe
--- /dev/null
+++ b/toolkit/components/AdaptiveTabs/utils.ts
@@ -0,0 +1,17 @@
+import type { TabItem, TabItemMenu } from './types';
+
+import { middot } from 'lib/html-entities';
+
+export const menuButton: TabItemMenu = {
+ id: 'menu',
+ title: `${ middot }${ middot }${ middot }`,
+ component: null,
+};
+
+export const getTabValue = (tab: TabItem): string => {
+ if (Array.isArray(tab.id)) {
+ return tab.id[0];
+ }
+
+ return tab.id;
+};
diff --git a/toolkit/components/RoutedTabs/RoutedTabs.tsx b/toolkit/components/RoutedTabs/RoutedTabs.tsx
new file mode 100644
index 0000000000..078a76e124
--- /dev/null
+++ b/toolkit/components/RoutedTabs/RoutedTabs.tsx
@@ -0,0 +1,63 @@
+import { pickBy } from 'es-toolkit';
+import { useRouter } from 'next/router';
+import React from 'react';
+
+import type { Props as AdaptiveTabsProps } from '../AdaptiveTabs/AdaptiveTabs';
+import AdaptiveTabs from '../AdaptiveTabs/AdaptiveTabs';
+import { getTabValue } from '../AdaptiveTabs/utils';
+import useActiveTabFromQuery from './useActiveTabFromQuery';
+
+interface Props extends AdaptiveTabsProps {}
+
+const RoutedTabs = (props: Props) => {
+ const { tabs, onValueChange, ...rest } = props;
+
+ const router = useRouter();
+ const activeTab = useActiveTabFromQuery(props.tabs);
+ const tabsRef = React.useRef(null);
+
+ const handleValueChange = React.useCallback(({ value }: { value: string }) => {
+ const nextTab = tabs.find((tab) => getTabValue(tab) === value);
+
+ if (!nextTab) {
+ return;
+ }
+
+ const queryForPathname = pickBy(router.query, (value, key) => router.pathname.includes(`[${ key }]`));
+ router.push(
+ { pathname: router.pathname, query: { ...queryForPathname, tab: value } },
+ undefined,
+ { shallow: true },
+ );
+
+ onValueChange?.({ value });
+ }, [ tabs, router, onValueChange ]);
+
+ React.useEffect(() => {
+ if (router.query.scroll_to_tabs) {
+ tabsRef?.current?.scrollIntoView(true);
+ delete router.query.scroll_to_tabs;
+ router.push(
+ {
+ pathname: router.pathname,
+ query: router.query,
+ },
+ undefined,
+ { shallow: true },
+ );
+ }
+ // replicate componentDidMount
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, []);
+
+ return (
+
+ );
+};
+
+export default React.memo(RoutedTabs);
diff --git a/ui/shared/Tabs/TabsSkeleton.tsx b/toolkit/components/RoutedTabs/RoutedTabsSkeleton.tsx
similarity index 54%
rename from ui/shared/Tabs/TabsSkeleton.tsx
rename to toolkit/components/RoutedTabs/RoutedTabsSkeleton.tsx
index eabc8fd352..6c2ec742b6 100644
--- a/ui/shared/Tabs/TabsSkeleton.tsx
+++ b/toolkit/components/RoutedTabs/RoutedTabsSkeleton.tsx
@@ -1,20 +1,21 @@
-import { Flex, chakra, Box, useColorModeValue } from '@chakra-ui/react';
+import { Flex, chakra, Box } from '@chakra-ui/react';
import React from 'react';
-import type { RoutedTab } from '../Tabs/types';
+import type { TabItemRegular } from '../AdaptiveTabs/types';
-import Skeleton from 'ui/shared/chakra/Skeleton';
-import useTabIndexFromQuery from 'ui/shared/Tabs/useTabIndexFromQuery';
+import { Skeleton } from 'toolkit/chakra/skeleton';
+import type { TabsProps } from 'toolkit/chakra/tabs';
-type TabSize = 'sm' | 'md';
+import useActiveTabFromQuery from './useActiveTabFromQuery';
-const SkeletonTabText = ({ size, title }: { size: TabSize; title: RoutedTab['title'] }) => (
+const SkeletonTabText = ({ size, title }: { size: TabsProps['size']; title: TabItemRegular['title'] }) => (
{ typeof title === 'string' ? title : title() }
@@ -22,18 +23,19 @@ const SkeletonTabText = ({ size, title }: { size: TabSize; title: RoutedTab['tit
interface Props {
className?: string;
- tabs: Array;
+ tabs: Array;
size?: 'sm' | 'md';
}
-const TabsSkeleton = ({ className, tabs, size = 'md' }: Props) => {
- const bgColor = useColorModeValue('blackAlpha.50', 'whiteAlpha.50');
- const tabIndex = useTabIndexFromQuery(tabs || []);
+const RoutedTabsSkeleton = ({ className, tabs, size = 'md' }: Props) => {
+ const activeTab = useActiveTabFromQuery(tabs);
if (tabs.length === 1) {
return null;
}
+ const tabIndex = activeTab ? tabs.findIndex((tab) => tab.id === activeTab.id) : 0;
+
return (
{ tabs.slice(0, tabIndex).map(({ title, id }) => (
@@ -44,7 +46,13 @@ const TabsSkeleton = ({ className, tabs, size = 'md' }: Props) => {
/>
)) }
{ tabs.slice(tabIndex, tabIndex + 1).map(({ title, id }) => (
-
+
{
);
};
-export default chakra(TabsSkeleton);
+export default chakra(RoutedTabsSkeleton);
diff --git a/toolkit/components/RoutedTabs/useActiveTabFromQuery.tsx b/toolkit/components/RoutedTabs/useActiveTabFromQuery.tsx
new file mode 100644
index 0000000000..14e88412d0
--- /dev/null
+++ b/toolkit/components/RoutedTabs/useActiveTabFromQuery.tsx
@@ -0,0 +1,22 @@
+import { useRouter } from 'next/router';
+
+import type { TabItem } from '../AdaptiveTabs/types';
+
+import getQueryParamString from 'lib/router/getQueryParamString';
+
+export default function useActiveTabFromQuery(tabs: Array) {
+ const router = useRouter();
+ const tabFromQuery = getQueryParamString(router.query.tab);
+
+ if (!tabFromQuery) {
+ return;
+ }
+
+ return tabs.find((tab) => {
+ if (Array.isArray(tab.id)) {
+ return tab.id.includes(tabFromQuery);
+ }
+
+ return tab.id === tabFromQuery || ('subTabs' in tab && tab.subTabs?.some((id) => id === tabFromQuery));
+ });
+}
diff --git a/toolkit/hooks/useClipboard.tsx b/toolkit/hooks/useClipboard.tsx
new file mode 100644
index 0000000000..e7f1ba9d30
--- /dev/null
+++ b/toolkit/hooks/useClipboard.tsx
@@ -0,0 +1,56 @@
+import { useCopyToClipboard } from '@uidotdev/usehooks';
+import React from 'react';
+
+import { SECOND } from 'lib/consts';
+import useIsMobile from 'lib/hooks/useIsMobile';
+
+import { useDisclosure } from './useDisclosure';
+
+// NOTE: If you don't need the disclosure and the timeout features, please use the useCopyToClipboard hook directly
+export default function useClipboard(text: string, timeout = SECOND) {
+ const flagTimeoutRef = React.useRef(null);
+ const disclosureTimeoutRef = React.useRef(null);
+ const [ hasCopied, setHasCopied ] = React.useState(false);
+
+ const isMobile = useIsMobile();
+ const [ , copyToClipboard ] = useCopyToClipboard();
+ const { open, onOpenChange } = useDisclosure();
+
+ const copy = React.useCallback(() => {
+ copyToClipboard(text);
+ setHasCopied(true);
+ // there is no hover on mobile, so we need to open the disclosure manually after click
+ isMobile && onOpenChange({ open: true });
+
+ disclosureTimeoutRef.current = window.setTimeout(() => {
+ onOpenChange({ open: false });
+ }, timeout);
+
+ // We need to wait for the disclosure to close before setting the flag to false
+ flagTimeoutRef.current = window.setTimeout(() => {
+ setHasCopied(false);
+ }, timeout + 200);
+ }, [ text, copyToClipboard, timeout, onOpenChange, isMobile ]);
+
+ React.useEffect(() => {
+ return () => {
+ if (disclosureTimeoutRef.current) {
+ window.clearTimeout(disclosureTimeoutRef.current);
+ }
+ if (flagTimeoutRef.current) {
+ window.clearTimeout(flagTimeoutRef.current);
+ }
+ };
+ }, []);
+
+ return React.useMemo(() => {
+ return {
+ hasCopied,
+ copy,
+ disclosure: {
+ open,
+ onOpenChange,
+ },
+ };
+ }, [ hasCopied, copy, open, onOpenChange ]);
+}
diff --git a/toolkit/hooks/useDisclosure.tsx b/toolkit/hooks/useDisclosure.tsx
new file mode 100644
index 0000000000..6e4cbc6f65
--- /dev/null
+++ b/toolkit/hooks/useDisclosure.tsx
@@ -0,0 +1,24 @@
+/* eslint-disable no-restricted-imports */
+import type { UseDisclosureProps } from '@chakra-ui/react';
+import { useDisclosure as useDisclosureChakra } from '@chakra-ui/react';
+import React from 'react';
+
+export function useDisclosure(props?: UseDisclosureProps) {
+ const { open, onOpen, onClose, onToggle } = useDisclosureChakra(props);
+
+ const onOpenChange = React.useCallback(({ open }: { open: boolean }) => {
+ if (open) {
+ onOpen();
+ } else {
+ onClose();
+ }
+ }, [ onOpen, onClose ]);
+
+ return React.useMemo(() => ({
+ open,
+ onOpenChange,
+ onClose,
+ onOpen,
+ onToggle,
+ }), [ open, onOpenChange, onClose, onOpen, onToggle ]);
+}
diff --git a/toolkit/hooks/useViewportSize.tsx b/toolkit/hooks/useViewportSize.tsx
new file mode 100644
index 0000000000..d494bf806b
--- /dev/null
+++ b/toolkit/hooks/useViewportSize.tsx
@@ -0,0 +1,22 @@
+import { debounce } from 'es-toolkit';
+import { useEffect, useState } from 'react';
+
+export default function useViewportSize(debounceTime = 100) {
+ const [ viewportSize, setViewportSize ] = useState({ width: 0, height: 0 });
+
+ useEffect(() => {
+ setViewportSize({ width: window.innerWidth, height: window.innerHeight });
+
+ const resizeHandler = debounce(() => {
+ setViewportSize({ width: window.innerWidth, height: window.innerHeight });
+ }, debounceTime);
+ const resizeObserver = new ResizeObserver(resizeHandler);
+
+ resizeObserver.observe(document.body);
+ return function cleanup() {
+ resizeObserver.unobserve(document.body);
+ };
+ }, [ debounceTime ]);
+
+ return viewportSize;
+}
diff --git a/toolkit/theme/foundations/animations.ts b/toolkit/theme/foundations/animations.ts
new file mode 100644
index 0000000000..0d46030a62
--- /dev/null
+++ b/toolkit/theme/foundations/animations.ts
@@ -0,0 +1,12 @@
+export const keyframes = {
+ fromLeftToRight: {
+ from: {
+ left: '0%',
+ transform: 'translateX(0%)',
+ },
+ to: {
+ left: '100%',
+ transform: 'translateX(-100%)',
+ },
+ },
+};
diff --git a/toolkit/theme/foundations/borders.ts b/toolkit/theme/foundations/borders.ts
new file mode 100644
index 0000000000..e048050ca2
--- /dev/null
+++ b/toolkit/theme/foundations/borders.ts
@@ -0,0 +1,13 @@
+import type { ThemingConfig } from '@chakra-ui/react';
+
+import type { ExcludeUndefined } from 'types/utils';
+
+export const radii: ExcludeUndefined['radii'] = {
+ none: { value: '0' },
+ sm: { value: '4px' },
+ base: { value: '8px' },
+ md: { value: '12px' },
+ lg: { value: '16px' },
+ xl: { value: '24px' },
+ full: { value: '9999px' },
+};
diff --git a/theme/foundations/breakpoints.ts b/toolkit/theme/foundations/breakpoints.ts
similarity index 79%
rename from theme/foundations/breakpoints.ts
rename to toolkit/theme/foundations/breakpoints.ts
index ece6201c12..3515f2c3ed 100644
--- a/theme/foundations/breakpoints.ts
+++ b/toolkit/theme/foundations/breakpoints.ts
@@ -1,7 +1,5 @@
const breakpoints = {
-// maybe we need them in future
sm: '415px',
- // md: '768px',
lg: '1000px',
xl: '1440px',
'2xl': '1920px',
diff --git a/toolkit/theme/foundations/colors.ts b/toolkit/theme/foundations/colors.ts
new file mode 100644
index 0000000000..a957d1c37c
--- /dev/null
+++ b/toolkit/theme/foundations/colors.ts
@@ -0,0 +1,162 @@
+const colors = {
+ green: {
+ '50': { value: '#F0FFF4' },
+ '100': { value: '#C6F6D5' },
+ '200': { value: '#9AE6B4' },
+ '300': { value: '#68D391' },
+ '400': { value: '#48BB78' },
+ '500': { value: '#38A169' },
+ '600': { value: '#25855A' },
+ '700': { value: '#276749' },
+ '800': { value: '#22543D' },
+ '900': { value: '#1C4532' },
+ },
+ blue: {
+ '50': { value: '#EBF8FF' },
+ '100': { value: '#BEE3F8' },
+ '200': { value: '#90CDF4' },
+ '300': { value: '#63B3ED' },
+ '400': { value: '#4299E1' },
+ '500': { value: '#3182CE' },
+ '600': { value: '#2B6CB0' },
+ '700': { value: '#2C5282' },
+ '800': { value: '#2A4365' },
+ '900': { value: '#1A365D' },
+ },
+ red: {
+ '50': { value: '#FFF5F5' },
+ '100': { value: '#FED7D7' },
+ '200': { value: '#FEB2B2' },
+ '300': { value: '#FC8181' },
+ '400': { value: '#F56565' },
+ '500': { value: '#E53E3E' },
+ '600': { value: '#C53030' },
+ '700': { value: '#9B2C2C' },
+ '800': { value: '#822727' },
+ '900': { value: '#63171B' },
+ },
+ orange: {
+ '50': { value: '#FFFAF0' },
+ '100': { value: '#FEEBCB' },
+ '200': { value: '#FBD38D' },
+ '300': { value: '#F6AD55' },
+ '400': { value: '#ED8936' },
+ '500': { value: '#DD6B20' },
+ '600': { value: '#C05621' },
+ '700': { value: '#9C4221' },
+ '800': { value: '#7B341E' },
+ '900': { value: '#652B19' },
+ },
+ yellow: {
+ '50': { value: '#FFFFF0' },
+ '100': { value: '#FEFCBF' },
+ '200': { value: '#FAF089' },
+ '300': { value: '#F6E05E' },
+ '400': { value: '#ECC94B' },
+ '500': { value: '#D69E2E' },
+ '600': { value: '#B7791F' },
+ '700': { value: '#975A16' },
+ '800': { value: '#744210' },
+ '900': { value: '#5F370E' },
+ },
+ gray: {
+ '50': { value: '#F7FAFC' },
+ '100': { value: '#EDF2F7' },
+ '200': { value: '#E2E8F0' },
+ '300': { value: '#CBD5E0' },
+ '400': { value: '#A0AEC0' },
+ '500': { value: '#718096' },
+ '600': { value: '#4A5568' },
+ '700': { value: '#2D3748' },
+ '800': { value: '#1A202C' },
+ '900': { value: '#171923' },
+ },
+ teal: {
+ '50': { value: '#E6FFFA' },
+ '100': { value: '#B2F5EA' },
+ '200': { value: '#81E6D9' },
+ '300': { value: '#4FD1C5' },
+ '400': { value: '#38B2AC' },
+ '500': { value: '#319795' },
+ '600': { value: '#2C7A7B' },
+ '700': { value: '#285E61' },
+ '800': { value: '#234E52' },
+ '900': { value: '#1D4044' },
+ },
+ cyan: {
+ '50': { value: '#EDFDFD' },
+ '100': { value: '#C4F1F9' },
+ '200': { value: '#9DECF9' },
+ '300': { value: '#76E4F7' },
+ '400': { value: '#0BC5EA' },
+ '500': { value: '#00B5D8' },
+ '600': { value: '#00A3C4' },
+ '700': { value: '#0987A0' },
+ '800': { value: '#086F83' },
+ '900': { value: '#065666' },
+ },
+ purple: {
+ '50': { value: '#FAF5FF' },
+ '100': { value: '#E9D8FD' },
+ '200': { value: '#D6BCFA' },
+ '300': { value: '#B794F4' },
+ '400': { value: '#9F7AEA' },
+ '500': { value: '#805AD5' },
+ '600': { value: '#6B46C1' },
+ '700': { value: '#553C9A' },
+ '800': { value: '#44337A' },
+ '900': { value: '#322659' },
+ },
+ pink: {
+ '50': { value: '#FFF5F7' },
+ '100': { value: '#FED7E2' },
+ '200': { value: '#FBB6CE' },
+ '300': { value: '#F687B3' },
+ '400': { value: '#ED64A6' },
+ '500': { value: '#D53F8C' },
+ '600': { value: '#B83280' },
+ '700': { value: '#97266D' },
+ '800': { value: '#702459' },
+ '900': { value: '#521B41' },
+ },
+ black: { value: '#101112' },
+ white: { value: '#ffffff' },
+ whiteAlpha: {
+ '50': { value: 'RGBA(255, 255, 255, 0.04)' },
+ '100': { value: 'RGBA(255, 255, 255, 0.06)' },
+ '200': { value: 'RGBA(255, 255, 255, 0.08)' },
+ '300': { value: 'RGBA(255, 255, 255, 0.16)' },
+ '400': { value: 'RGBA(255, 255, 255, 0.24)' },
+ '500': { value: 'RGBA(255, 255, 255, 0.36)' },
+ '600': { value: 'RGBA(255, 255, 255, 0.48)' },
+ '700': { value: 'RGBA(255, 255, 255, 0.64)' },
+ '800': { value: 'RGBA(255, 255, 255, 0.80)' },
+ '900': { value: 'RGBA(255, 255, 255, 0.92)' },
+ },
+ blackAlpha: {
+ '50': { value: 'RGBA(16, 17, 18, 0.04)' },
+ '100': { value: 'RGBA(16, 17, 18, 0.06)' },
+ '200': { value: 'RGBA(16, 17, 18, 0.08)' },
+ '300': { value: 'RGBA(16, 17, 18, 0.16)' },
+ '400': { value: 'RGBA(16, 17, 18, 0.24)' },
+ '500': { value: 'RGBA(16, 17, 18, 0.36)' },
+ '600': { value: 'RGBA(16, 17, 18, 0.48)' },
+ '700': { value: 'RGBA(16, 17, 18, 0.64)' },
+ '800': { value: 'RGBA(16, 17, 18, 0.80)' },
+ '900': { value: 'RGBA(16, 17, 18, 0.92)' },
+ },
+ // BRAND COLORS
+ github: { value: '#171923' },
+ telegram: { value: '#2775CA' },
+ linkedin: { value: '#1564BA' },
+ discord: { value: '#9747FF' },
+ slack: { value: '#1BA27A' },
+ twitter: { value: '#000000' },
+ opensea: { value: '#2081E2' },
+ facebook: { value: '#4460A0' },
+ medium: { value: '#231F20' },
+ reddit: { value: '#FF4500' },
+ celo: { value: '#FCFF52' },
+};
+
+export default colors;
diff --git a/toolkit/theme/foundations/durations.ts b/toolkit/theme/foundations/durations.ts
new file mode 100644
index 0000000000..bc60ecad4a
--- /dev/null
+++ b/toolkit/theme/foundations/durations.ts
@@ -0,0 +1,15 @@
+import type { ThemingConfig } from '@chakra-ui/react';
+
+import type { ExcludeUndefined } from 'types/utils';
+
+const durations: ExcludeUndefined['durations'] = {
+ 'ultra-fast': { value: '50ms' },
+ faster: { value: '100ms' },
+ fast: { value: '150ms' },
+ normal: { value: '200ms' },
+ slow: { value: '300ms' },
+ slower: { value: '400ms' },
+ 'ultra-slow': { value: '500ms' },
+};
+
+export default durations;
diff --git a/toolkit/theme/foundations/semanticTokens.ts b/toolkit/theme/foundations/semanticTokens.ts
new file mode 100644
index 0000000000..b0b43dbe55
--- /dev/null
+++ b/toolkit/theme/foundations/semanticTokens.ts
@@ -0,0 +1,471 @@
+import type { ThemingConfig } from '@chakra-ui/react';
+
+import config from 'configs/app';
+
+const heroBannerButton = config.UI.homepage.heroBanner?.button;
+
+const semanticTokens: ThemingConfig['semanticTokens'] = {
+ colors: {
+ button: {
+ outline: {
+ fg: {
+ DEFAULT: { value: { _light: '{colors.blue.600}', _dark: '{colors.blue.600}' } },
+ },
+ },
+ subtle: {
+ fg: {
+ DEFAULT: { value: { _light: '{colors.blackAlpha.800}', _dark: '{colors.whiteAlpha.800}' } },
+ },
+ bg: {
+ DEFAULT: { value: { _light: '{colors.blackAlpha.200}', _dark: '{colors.whiteAlpha.200}' } },
+ },
+ },
+ dropdown: {
+ fg: {
+ DEFAULT: { value: { _light: '{colors.blackAlpha.800}', _dark: '{colors.whiteAlpha.800}' } },
+ selected: { value: { _light: '{colors.blue.700}', _dark: '{colors.whiteAlpha.800}' } },
+ },
+ bg: {
+ selected: { value: { _light: '{colors.blue.50}', _dark: '{colors.whiteAlpha.100}' } },
+ },
+ border: {
+ DEFAULT: { value: { _light: '{colors.gray.200}', _dark: '{colors.gray.600}' } },
+ },
+ },
+ header: {
+ fg: {
+ DEFAULT: { value: { _light: '{colors.blackAlpha.800}', _dark: '{colors.gray.400}' } },
+ selected: { value: { _light: '{colors.blackAlpha.800}', _dark: '{colors.whiteAlpha.800}' } },
+ highlighted: { value: { _light: '{colors.blackAlpha.800}', _dark: '{colors.whiteAlpha.800}' } },
+ },
+ bg: {
+ selected: { value: { _light: '{colors.blackAlpha.50}', _dark: '{colors.whiteAlpha.100}' } },
+ highlighted: { value: { _light: '{colors.orange.100}', _dark: '{colors.orange.900}' } },
+ },
+ border: {
+ DEFAULT: { value: { _light: '{colors.gray.300}', _dark: '{colors.gray.600}' } },
+ },
+ },
+ segmented: {
+ fg: {
+ DEFAULT: { value: { _light: '{colors.blue.600}', _dark: '{colors.blue.300}' } },
+ selected: { value: { _light: '{colors.blue.700}', _dark: '{colors.gray.50}' } },
+ },
+ border: {
+ DEFAULT: { value: { _light: '{colors.blue.50}', _dark: '{colors.gray.800}' } },
+ },
+ },
+ icon_secondary: {
+ fg: {
+ DEFAULT: { value: { _light: '{colors.gray.400}', _dark: '{colors.gray.500}' } },
+ selected: { value: { _light: '{colors.blue.700}', _dark: '{colors.whiteAlpha.800}' } },
+ },
+ bg: {
+ selected: { value: { _light: '{colors.blue.50}', _dark: '{colors.whiteAlpha.100}' } },
+ },
+ },
+ hero: {
+ bg: {
+ DEFAULT: {
+ value: {
+ _light: heroBannerButton?._default?.background?.[0] || '{colors.blue.600}',
+ _dark: heroBannerButton?._default?.background?.[1] || heroBannerButton?._default?.background?.[0] || '{colors.blue.600}',
+ },
+ },
+ hover: {
+ value: {
+ _light: heroBannerButton?._hover?.background?.[0] || '{colors.blue.400}',
+ _dark: heroBannerButton?._hover?.background?.[1] || heroBannerButton?._hover?.background?.[0] || '{colors.blue.400}',
+ },
+ },
+ selected: {
+ value: {
+ _light: heroBannerButton?._selected?.background?.[0] || '{colors.blue.50}',
+ _dark: heroBannerButton?._selected?.background?.[1] || heroBannerButton?._selected?.background?.[0] || '{colors.blue.50}',
+ },
+ },
+ },
+ fg: {
+ DEFAULT: {
+ value: {
+ _light: heroBannerButton?._default?.text_color?.[0] || '{colors.white}',
+ _dark: heroBannerButton?._default?.text_color?.[1] || heroBannerButton?._default?.text_color?.[0] || '{colors.white}',
+ },
+ },
+ hover: {
+ value: {
+ _light: heroBannerButton?._hover?.text_color?.[0] || '{colors.white}',
+ _dark: heroBannerButton?._hover?.text_color?.[1] || heroBannerButton?._hover?.text_color?.[0] || '{colors.white}',
+ },
+ },
+ selected: {
+ value: {
+ _light: heroBannerButton?._selected?.text_color?.[0] || '{colors.blackAlpha.800}',
+ _dark: heroBannerButton?._selected?.text_color?.[1] || heroBannerButton?._selected?.text_color?.[0] || '{colors.blackAlpha.800}',
+ },
+ },
+ },
+ },
+ },
+ closeButton: {
+ fg: {
+ DEFAULT: { value: { _light: '{colors.blackAlpha.500}', _dark: '{colors.whiteAlpha.500}' } },
+ },
+ },
+ link: {
+ primary: {
+ DEFAULT: { value: { _light: '{colors.blue.600}', _dark: '{colors.blue.300}' } },
+ hover: { value: { _light: '{colors.blue.400}' } },
+ },
+ secondary: {
+ DEFAULT: { value: { _light: '{colors.gray.500}', _dark: '{colors.gray.400}' } },
+ },
+ underlaid: {
+ bg: { value: { _light: '{colors.gray.100}', _dark: '{colors.gray.800}' } },
+ },
+ subtle: {
+ DEFAULT: { value: { _light: '{colors.blackAlpha.800}', _dark: '{colors.gray.400}' } },
+ hover: { value: { _light: '{colors.blackAlpha.800}', _dark: '{colors.gray.400}' } },
+ },
+ navigation: {
+ fg: {
+ DEFAULT: { value: { _light: '{colors.gray.600}', _dark: '{colors.gray.400}' } },
+ selected: { value: { _light: '{colors.blue.700}', _dark: '{colors.gray.50}' } },
+ hover: { value: { _light: '{colors.link.primary.hover}' } },
+ active: { value: { _light: '{colors.link.primary.hover}' } },
+ },
+ bg: {
+ selected: { value: { _light: '{colors.blue.50}', _dark: '{colors.gray.800}' } },
+ group: { value: { _light: '{colors.white}', _dark: '{colors.black}' } },
+ },
+ },
+ menu: {
+ DEFAULT: { value: { _light: '{colors.blackAlpha.800}', _dark: '{colors.whiteAlpha.800}' } },
+ },
+ },
+ tooltip: {
+ DEFAULT: {
+ bg: { value: '{colors.gray.900}' },
+ fg: { value: '{colors.white}' },
+ },
+ navigation: {
+ bg: { value: { _light: '{colors.blue.50}', _dark: '{colors.gray.800}' } },
+ fg: {
+ DEFAULT: { value: '{colors.blue.400}' },
+ selected: { value: { _light: '{colors.blue.700}', _dark: '{colors.gray.50}' } },
+ },
+ },
+ },
+ popover: {
+ DEFAULT: {
+ bg: { value: { _light: '{colors.white}', _dark: '{colors.gray.900}' } },
+ shadow: { value: { _light: '{colors.blackAlpha.200}', _dark: '{colors.whiteAlpha.300}' } },
+ },
+ },
+ progressCircle: {
+ trackColor: {
+ DEFAULT: { value: { _light: '{colors.gray.100}', _dark: '{colors.whiteAlpha.100}' } },
+ },
+ },
+ skeleton: {
+ bg: {
+ start: { value: { _light: '{colors.blackAlpha.50}', _dark: '{colors.whiteAlpha.50}' } },
+ end: { value: { _light: '{colors.blackAlpha.100}', _dark: '{colors.whiteAlpha.100}' } },
+ },
+ },
+ tabs: {
+ solid: {
+ fg: {
+ DEFAULT: { value: { _light: '{colors.blue.700}', _dark: '{colors.blue.100}' } },
+ selected: { value: { _light: '{colors.blue.700}', _dark: '{colors.gray.50}' } },
+ },
+ bg: {
+ selected: { value: { _light: '{colors.blue.50}', _dark: '{colors.whiteAlpha.100}' } },
+ },
+ },
+ secondary: {
+ fg: {
+ DEFAULT: { value: { _light: '{colors.blackAlpha.800}', _dark: '{colors.whiteAlpha.800}' } },
+ },
+ bg: {
+ selected: { value: { _light: '{colors.blue.50}', _dark: '{colors.whiteAlpha.100}' } },
+ },
+ border: {
+ DEFAULT: { value: { _light: '{colors.gray.300}', _dark: '{colors.gray.600}' } },
+ },
+ },
+ segmented: {
+ fg: {
+ DEFAULT: { value: { _light: '{colors.blue.600}', _dark: '{colors.blue.300}' } },
+ selected: { value: { _light: '{colors.blue.700}', _dark: '{colors.gray.50}' } },
+ },
+ border: {
+ DEFAULT: { value: { _light: '{colors.blue.50}', _dark: '{colors.gray.800}' } },
+ },
+ },
+ },
+ 'switch': {
+ primary: {
+ bg: {
+ DEFAULT: { value: { _light: '{colors.gray.300}', _dark: '{colors.whiteAlpha.400}' } },
+ checked: { value: { _light: '{colors.blue.500}', _dark: '{colors.blue.300}' } },
+ hover: { value: { _light: '{colors.blue.600}', _dark: '{colors.blue.400}' } },
+ },
+ },
+ },
+ alert: {
+ fg: {
+ DEFAULT: { value: { _light: '{colors.blackAlpha.800}', _dark: '{colors.whiteAlpha.800}' } },
+ },
+ bg: {
+ info: { value: { _light: '{colors.blackAlpha.50}', _dark: '{colors.whiteAlpha.100}' } },
+ warning: { value: { _light: '{colors.orange.100}', _dark: '{colors.orange.800/60}' } },
+ warning_table: { value: { _light: '{colors.orange.50}', _dark: '{colors.orange.800/60}' } },
+ success: { value: { _light: '{colors.green.100}', _dark: '{colors.green.900}' } },
+ error: { value: { _light: '{colors.red.100}', _dark: '{colors.red.900}' } },
+ },
+ },
+ toast: {
+ fg: {
+ DEFAULT: { value: '{colors.alert.fg}' },
+ },
+ bg: {
+ DEFAULT: { value: '{colors.alert.bg.info}' },
+ info: { value: { _light: '{colors.blue.100}', _dark: '{colors.blue.900}' } },
+ warning: { value: '{colors.alert.bg.warning}' },
+ success: { value: '{colors.alert.bg.success}' },
+ error: { value: '{colors.alert.bg.error}' },
+ loading: { value: { _light: '{colors.blue.100}', _dark: '{colors.blue.900}' } },
+ },
+ },
+ input: {
+ fg: {
+ DEFAULT: { value: { _light: '{colors.gray.800}', _dark: '{colors.gray.50}' } },
+ error: { value: '{colors.text.error}' },
+ },
+ bg: {
+ DEFAULT: { value: { _light: '{colors.white}', _dark: '{colors.black}' } },
+ readOnly: { value: { _light: '{colors.gray.200}', _dark: '{colors.gray.800}' } },
+ },
+ border: {
+ DEFAULT: { value: { _light: '{colors.gray.100}', _dark: '{colors.gray.700}' } },
+ hover: { value: { _light: '{colors.gray.200}', _dark: '{colors.gray.500}' } },
+ focus: { value: '{colors.blue.400}' },
+ filled: { value: { _light: '{colors.gray.300}', _dark: '{colors.gray.600}' } },
+ readOnly: { value: { _light: '{colors.gray.200}', _dark: '{colors.gray.800}' } },
+ error: { value: '{colors.red.500}' },
+ },
+ placeholder: {
+ DEFAULT: { value: '{colors.gray.500}' },
+ error: { value: '{colors.red.500}' },
+ },
+ },
+ field: {
+ placeholder: {
+ DEFAULT: { value: '{colors.gray.500}' },
+ disabled: { value: '{colors.gray.500/20}' },
+ error: { value: '{colors.red.500}' },
+ },
+ },
+ dialog: {
+ bg: {
+ DEFAULT: { value: { _light: '{colors.white}', _dark: '{colors.gray.900}' } },
+ },
+ fg: {
+ DEFAULT: { value: { _light: '{colors.blackAlpha.800}', _dark: '{colors.whiteAlpha.800}' } },
+ },
+ },
+ drawer: {
+ bg: {
+ DEFAULT: { value: { _light: '{colors.white}', _dark: '{colors.gray.900}' } },
+ },
+ },
+ select: {
+ trigger: {
+ outline: {
+ fg: { value: { _light: '{colors.blackAlpha.800}', _dark: '{colors.whiteAlpha.800}' } },
+ },
+ },
+ item: {
+ bg: {
+ highlighted: { value: { _light: '{colors.blue.50}', _dark: '{colors.whiteAlpha.100}' } },
+ },
+ },
+ indicator: {
+ fg: {
+ DEFAULT: { value: '{colors.gray.500}' },
+ },
+ },
+ placeholder: {
+ fg: {
+ DEFAULT: { value: '{colors.gray.500}' },
+ error: { value: '{colors.red.500}' },
+ },
+ },
+ },
+ menu: {
+ item: {
+ bg: {
+ highlighted: { value: { _light: '{colors.blue.50}', _dark: '{colors.whiteAlpha.100}' } },
+ },
+ },
+ },
+ spinner: {
+ track: {
+ DEFAULT: { value: { _light: '{colors.blackAlpha.200}', _dark: '{colors.whiteAlpha.200}' } },
+ },
+ },
+ badge: {
+ gray: {
+ bg: { value: { _light: '{colors.blackAlpha.50}', _dark: '{colors.whiteAlpha.100}' } },
+ fg: { value: { _light: '{colors.blackAlpha.800}', _dark: '{colors.whiteAlpha.800}' } },
+ },
+ green: {
+ bg: { value: { _light: '{colors.green.50}', _dark: '{colors.green.800}' } },
+ fg: { value: { _light: '{colors.green.500}', _dark: '{colors.green.200}' } },
+ },
+ red: {
+ bg: { value: { _light: '{colors.red.50}', _dark: '{colors.red.800}' } },
+ fg: { value: { _light: '{colors.red.500}', _dark: '{colors.red.200}' } },
+ },
+ purple: {
+ bg: { value: { _light: '{colors.purple.50}', _dark: '{colors.purple.800}' } },
+ fg: { value: { _light: '{colors.purple.500}', _dark: '{colors.purple.100}' } },
+ },
+ purple_alt: {
+ bg: { value: { _light: '{colors.purple.100}', _dark: '{colors.purple.800}' } },
+ fg: { value: { _light: '{colors.blackAlpha.800}', _dark: '{colors.whiteAlpha.800}' } },
+ },
+ orange: {
+ bg: { value: { _light: '{colors.orange.50}', _dark: '{colors.orange.800}' } },
+ fg: { value: { _light: '{colors.orange.500}', _dark: '{colors.orange.100}' } },
+ },
+ blue: {
+ bg: { value: { _light: '{colors.blue.50}', _dark: '{colors.blue.800}' } },
+ fg: { value: { _light: '{colors.blue.500}', _dark: '{colors.blue.100}' } },
+ },
+ blue_alt: {
+ bg: { value: { _light: '{colors.blue.50}', _dark: '{colors.blue.800}' } },
+ fg: { value: { _light: '{colors.blackAlpha.800}', _dark: '{colors.whiteAlpha.800}' } },
+ },
+ yellow: {
+ bg: { value: { _light: '{colors.yellow.50}', _dark: '{colors.yellow.800}' } },
+ fg: { value: { _light: '{colors.yellow.500}', _dark: '{colors.yellow.100}' } },
+ },
+ teal: {
+ bg: { value: { _light: '{colors.teal.50}', _dark: '{colors.teal.800}' } },
+ fg: { value: { _light: '{colors.teal.500}', _dark: '{colors.teal.100}' } },
+ },
+ cyan: {
+ bg: { value: { _light: '{colors.cyan.50}', _dark: '{colors.cyan.800}' } },
+ fg: { value: { _light: '{colors.cyan.500}', _dark: '{colors.cyan.100}' } },
+ },
+ },
+ tag: {
+ root: {
+ subtle: {
+ bg: { value: { _light: '{colors.blackAlpha.50}', _dark: '{colors.whiteAlpha.100}' } },
+ fg: { value: { _light: '{colors.blackAlpha.800}', _dark: '{colors.whiteAlpha.800}' } },
+ },
+ clickable: {
+ bg: { value: { _light: '{colors.gray.100}', _dark: '{colors.gray.800}' } },
+ fg: { value: { _light: '{colors.blackAlpha.800}', _dark: '{colors.whiteAlpha.800}' } },
+ },
+ filter: {
+ bg: { value: { _light: '{colors.blue.50}', _dark: '{colors.blue.800}' } },
+ },
+ select: {
+ bg: {
+ DEFAULT: { value: { _light: '{colors.gray.100}', _dark: '{colors.gray.800}' } },
+ selected: { value: { _light: '{colors.blue.500}', _dark: '{colors.blue.900}' } },
+ },
+ fg: { value: { _light: '{colors.gray.500}', _dark: '{colors.whiteAlpha.800}' } },
+ },
+ },
+ },
+ table: {
+ header: {
+ bg: { value: { _light: '{colors.blackAlpha.100}', _dark: '{colors.whiteAlpha.200}' } },
+ fg: { value: { _light: '{colors.blackAlpha.700}', _dark: '{colors.whiteAlpha.700}' } },
+ },
+ },
+ checkbox: {
+ control: {
+ border: {
+ DEFAULT: { value: { _light: '{colors.gray.100}', _dark: '{colors.gray.700}' } },
+ hover: { value: { _light: '{colors.gray.200}', _dark: '{colors.gray.500}' } },
+ readOnly: { value: { _light: '{colors.gray.200}', _dark: '{colors.gray.800}' } },
+ },
+ },
+ },
+ radio: {
+ control: {
+ border: {
+ DEFAULT: { value: { _light: '{colors.gray.100}', _dark: '{colors.gray.700}' } },
+ hover: { value: { _light: '{colors.gray.200}', _dark: '{colors.gray.500}' } },
+ readOnly: { value: { _light: '{colors.gray.200}', _dark: '{colors.gray.800}' } },
+ },
+ },
+ },
+ stat: {
+ indicator: {
+ up: { value: { _light: '{colors.green.500}', _dark: '{colors.green.400}' } },
+ down: { value: { _light: '{colors.red.600}', _dark: '{colors.red.400}' } },
+ },
+ },
+ rating: {
+ DEFAULT: { value: { _light: '{colors.gray.200}', _dark: '{colors.gray.700}' } },
+ highlighted: { value: '{colors.yellow.400}' },
+ },
+ heading: {
+ DEFAULT: { value: { _light: '{colors.blackAlpha.800}', _dark: '{colors.whiteAlpha.800}' } },
+ },
+ text: {
+ primary: { value: { _light: '{colors.blackAlpha.800}', _dark: '{colors.whiteAlpha.800}' } },
+ secondary: { value: { _light: '{colors.gray.500}', _dark: '{colors.gray.400}' } },
+ error: { value: '{colors.red.500}' },
+ },
+ border: {
+ divider: { value: { _light: '{colors.blackAlpha.100}', _dark: '{colors.whiteAlpha.100}' } },
+ error: { value: '{colors.red.500}' },
+ },
+ icon: {
+ backTo: { value: '{colors.gray.400}' },
+ externalLink: { value: { _light: '{colors.gray.400}', _dark: '{colors.gray.500}' } },
+ info: { value: { _light: '{colors.gray.400}', _dark: '{colors.gray.500}' } },
+ },
+ address: {
+ highlighted: {
+ bg: { value: { _light: '{colors.blue.50}', _dark: '{colors.blue.900}' } },
+ border: { value: { _light: '{colors.blue.200}', _dark: '{colors.blue.600}' } },
+ },
+ },
+ global: {
+ body: {
+ bg: { value: { _light: '{colors.white}', _dark: '{colors.black}' } },
+ fg: { value: '{colors.text.primary}' },
+ },
+ mark: {
+ bg: { value: { _light: '{colors.green.100}', _dark: '{colors.green.800}' } },
+ },
+ scrollbar: {
+ thumb: { value: { _light: '{colors.blackAlpha.300}', _dark: '{colors.whiteAlpha.300}' } },
+ },
+ },
+ },
+ shadows: {
+ popover: {
+ DEFAULT: { value: { _light: '{shadows.size.2xl}', _dark: '{shadows.dark-lg}' } },
+ },
+ drawer: {
+ DEFAULT: { value: { _light: '{shadows.size.lg}', _dark: '{shadows.dark-lg}' } },
+ },
+ },
+ opacity: {
+ control: {
+ disabled: { value: '0.2' },
+ },
+ },
+};
+
+export default semanticTokens;
diff --git a/toolkit/theme/foundations/shadows.ts b/toolkit/theme/foundations/shadows.ts
new file mode 100644
index 0000000000..3d7b674e4a
--- /dev/null
+++ b/toolkit/theme/foundations/shadows.ts
@@ -0,0 +1,19 @@
+import type { ThemingConfig } from '@chakra-ui/react';
+
+import type { ExcludeUndefined } from 'types/utils';
+
+const shadows: ExcludeUndefined['shadows'] = {
+ action_bar: { value: '0 4px 4px -4px rgb(0 0 0 / 10%), 0 2px 4px -4px rgb(0 0 0 / 6%)' },
+ size: {
+ xs: { value: '0px 0px 0px 1px rgba(0, 0, 0, 0.05)' },
+ sm: { value: '0px 1px 2px 0px rgba(0, 0, 0, 0.05)' },
+ base: { value: '0px 1px 2px 0px rgba(0, 0, 0, 0.06), 0px 1px 3px 0px rgba(0, 0, 0, 0.1)' },
+ md: { value: '0px 2px 4px -1px rgba(0, 0, 0, 0.06), 0px 4px 6px -1px rgba(0, 0, 0, 0.1)' },
+ lg: { value: '0px 4px 6px -2px rgba(0, 0, 0, 0.05), 0px 10px 15px -3px rgba(0, 0, 0, 0.1)' },
+ xl: { value: '0px 10px 10px -5px rgba(0, 0, 0, 0.04), 0px 20px 25px -5px rgba(0, 0, 0, 0.1)' },
+ '2xl': { value: '0px 15px 50px -12px rgba(0, 0, 0, 0.25)' },
+ },
+ 'dark-lg': { value: '0px 15px 40px 0px rgba(0, 0, 0, 0.4), 0px 5px 10px 0px rgba(0, 0, 0, 0.2), 0px 0px 0px 1px rgba(0, 0, 0, 0.1)' },
+};
+
+export default shadows;
diff --git a/toolkit/theme/foundations/typography.ts b/toolkit/theme/foundations/typography.ts
new file mode 100644
index 0000000000..8d859e7540
--- /dev/null
+++ b/toolkit/theme/foundations/typography.ts
@@ -0,0 +1,93 @@
+import type { ThemingConfig } from '@chakra-ui/react';
+
+import type { ExcludeUndefined } from 'types/utils';
+
+import config from 'configs/app';
+
+export const BODY_TYPEFACE = config.UI.fonts.body?.name ?? 'Inter';
+export const HEADING_TYPEFACE = config.UI.fonts.heading?.name ?? 'Poppins';
+
+export const fonts: ExcludeUndefined['fonts'] = {
+ heading: { value: `${ HEADING_TYPEFACE }, sans-serif` },
+ body: { value: `${ BODY_TYPEFACE }, sans-serif` },
+};
+
+export const textStyles: ThemingConfig['textStyles'] = {
+ heading: {
+ xl: {
+ value: {
+ fontSize: '32px',
+ lineHeight: '40px',
+ fontWeight: '500',
+ letterSpacing: '-0.5px',
+ fontFamily: 'heading',
+ },
+ },
+ lg: {
+ value: {
+ fontSize: '24px',
+ lineHeight: '32px',
+ fontWeight: '500',
+ fontFamily: 'heading',
+ },
+ },
+ md: {
+ value: {
+ fontSize: '18px',
+ lineHeight: '24px',
+ fontWeight: '500',
+ fontFamily: 'heading',
+ },
+ },
+ sm: {
+ value: {
+ fontSize: '16px',
+ lineHeight: '24px',
+ fontWeight: '500',
+ fontFamily: 'heading',
+ },
+ },
+ xs: {
+ value: {
+ fontSize: '14px',
+ lineHeight: '20px',
+ fontWeight: '600',
+ fontFamily: 'heading',
+ },
+ },
+ },
+ text: {
+ xl: {
+ value: {
+ fontSize: '20px',
+ lineHeight: '28px',
+ fontWeight: '400',
+ fontFamily: 'body',
+ },
+ },
+ md: {
+ value: {
+ fontSize: '16px',
+ lineHeight: '24px',
+ fontWeight: '400',
+ fontFamily: 'body',
+ },
+ },
+ sm: {
+ value: {
+ fontSize: '14px',
+ lineHeight: '20px',
+ fontWeight: '400',
+ fontFamily: 'body',
+ },
+ },
+ xs: {
+ value: {
+ fontSize: '12px',
+ lineHeight: '16px',
+ fontWeight: '400',
+ fontFamily: 'body',
+ },
+ },
+ },
+};
diff --git a/toolkit/theme/foundations/zIndex.ts b/toolkit/theme/foundations/zIndex.ts
new file mode 100644
index 0000000000..20d0f3f960
--- /dev/null
+++ b/toolkit/theme/foundations/zIndex.ts
@@ -0,0 +1,20 @@
+export const zIndex = {
+ hide: { value: -1 },
+ auto: { value: 'auto' },
+ base: { value: 0 },
+ docked: { value: 10 },
+ dropdown: { value: 1000 },
+ sticky: { value: 1100 },
+ sticky1: { value: 1101 },
+ sticky2: { value: 1102 },
+ banner: { value: 1200 },
+ overlay: { value: 1300 },
+ modal: { value: 1400 },
+ popover: { value: 1500 },
+ tooltip: { value: 1550 }, // otherwise tooltips will not be visible in modals
+ tooltip2: { value: 1551 }, // for tooltips in tooltips
+ skipLink: { value: 1600 },
+ toast: { value: 1700 },
+};
+
+export default zIndex;
diff --git a/toolkit/theme/globalCss.ts b/toolkit/theme/globalCss.ts
new file mode 100644
index 0000000000..5dec641b57
--- /dev/null
+++ b/toolkit/theme/globalCss.ts
@@ -0,0 +1,57 @@
+import type { SystemConfig } from '@chakra-ui/react';
+
+import addressEntity from './globals/address-entity';
+import recaptcha from './globals/recaptcha';
+import scrollbar from './globals/scrollbar';
+
+const webkitAutofillOverrides = {
+ WebkitTextFillColor: 'var(--chakra-colors-input-fg)',
+ '-webkit-box-shadow': '0 0 0px 1000px var(--chakra-colors-input-bg) inset',
+ transition: 'background-color 5000s ease-in-out 0s',
+};
+
+const webkitAutofillRules = {
+ '&:-webkit-autofill': webkitAutofillOverrides,
+ '&:-webkit-autofill:hover': webkitAutofillOverrides,
+ '&:-webkit-autofill:focus': webkitAutofillOverrides,
+};
+
+const globalCss: SystemConfig['globalCss'] = {
+ body: {
+ bg: 'global.body.bg',
+ color: 'global.body.fg',
+ WebkitTapHighlightColor: 'transparent',
+ fontVariantLigatures: 'no-contextual',
+ focusRingStyle: 'hidden',
+ },
+ mark: {
+ bg: 'global.mark.bg',
+ color: 'inherit',
+ },
+ 'svg *::selection': {
+ color: 'none',
+ background: 'none',
+ },
+ form: {
+ w: '100%',
+ },
+ input: {
+ // hide number input arrows in Google Chrome
+ '&::-webkit-outer-spin-button, &::-webkit-inner-spin-button': {
+ WebkitAppearance: 'none',
+ margin: 0,
+ },
+ ...webkitAutofillRules,
+ },
+ textarea: {
+ ...webkitAutofillRules,
+ },
+ select: {
+ ...webkitAutofillRules,
+ },
+ ...recaptcha,
+ ...scrollbar,
+ ...addressEntity,
+};
+
+export default globalCss;
diff --git a/toolkit/theme/globals/address-entity.ts b/toolkit/theme/globals/address-entity.ts
new file mode 100644
index 0000000000..4f1ee388e7
--- /dev/null
+++ b/toolkit/theme/globals/address-entity.ts
@@ -0,0 +1,33 @@
+const styles = {
+ '.address-entity': {
+ '&.address-entity_highlighted': {
+ _before: {
+ content: `" "`,
+ position: 'absolute',
+ py: 1,
+ pl: 1,
+ pr: 0,
+ top: '-5px',
+ left: '-5px',
+ width: `calc(100% + 6px)`,
+ height: 'calc(100% + 10px)',
+ borderRadius: 'base',
+ borderColor: 'address.highlighted.border',
+ borderWidth: '1px',
+ borderStyle: 'dashed',
+ bgColor: 'address.highlighted.bg',
+ zIndex: -1,
+ },
+ },
+ },
+ '.address-entity_no-copy': {
+ '&.address-entity_highlighted': {
+ _before: {
+ pr: 2,
+ width: `calc(100% + 6px + 8px)`,
+ },
+ },
+ },
+};
+
+export default styles;
diff --git a/toolkit/theme/globals/recaptcha.ts b/toolkit/theme/globals/recaptcha.ts
new file mode 100644
index 0000000000..838fb98d4d
--- /dev/null
+++ b/toolkit/theme/globals/recaptcha.ts
@@ -0,0 +1,20 @@
+const styles = {
+ '.grecaptcha-badge': {
+ visibility: 'hidden',
+ },
+ 'div:has(div):has(iframe[title="recaptcha challenge expires in two minutes"])': {
+ '&::after': {
+ content: `" "`,
+ display: 'block',
+ position: 'fixed',
+ top: 0,
+ left: 0,
+ width: '100vw',
+ height: '100vh',
+ zIndex: 100000,
+ bgColor: 'blackAlpha.300',
+ },
+ },
+};
+
+export default styles;
diff --git a/toolkit/theme/globals/scrollbar.ts b/toolkit/theme/globals/scrollbar.ts
new file mode 100644
index 0000000000..152145044b
--- /dev/null
+++ b/toolkit/theme/globals/scrollbar.ts
@@ -0,0 +1,32 @@
+const scrollbar = {
+ 'body *::-webkit-scrollbar': {
+ width: '20px',
+ },
+ 'body *::-webkit-scrollbar-track': {
+ backgroundColor: 'transparent',
+ },
+ 'body *::-webkit-scrollbar-thumb': {
+ backgroundColor: '{colors.global.scrollbar.thumb}',
+ borderRadius: '20px',
+ border: `8px solid rgba(0,0,0,0)`,
+ backgroundClip: 'content-box',
+ minHeight: '32px',
+ },
+ 'body *::-webkit-scrollbar-button': {
+ display: 'none',
+ },
+ 'body *::-webkit-scrollbar-corner': {
+ backgroundColor: 'transparent',
+ },
+ 'body *::-webkit-resizer': {
+ // FIXME for dark mode we need to use a different image - /static/resizer_dark.png
+ backgroundImage: 'url(/static/resizer_light.png)',
+ backgroundSize: '20px',
+ },
+ 'body *': {
+ scrollbarWidth: 'thin',
+ scrollbarColor: `{colors.global.scrollbar.thumb} transparent`,
+ },
+};
+
+export default scrollbar;
diff --git a/toolkit/theme/recipes/accordion.recipe.ts b/toolkit/theme/recipes/accordion.recipe.ts
new file mode 100644
index 0000000000..44d6e8c275
--- /dev/null
+++ b/toolkit/theme/recipes/accordion.recipe.ts
@@ -0,0 +1,145 @@
+import { defineSlotRecipe } from '@chakra-ui/react';
+
+export const recipe = defineSlotRecipe({
+ className: 'chakra-accordion',
+ slots: [ 'root', 'item', 'itemTrigger', 'itemContent', 'itemBody', 'itemIndicator' ],
+ base: {
+ root: {
+ width: 'full',
+ '--accordion-radius': 'none',
+ },
+ item: {
+ overflowAnchor: 'none',
+ borderColor: 'border.divider',
+ },
+ itemTrigger: {
+ display: 'flex',
+ alignItems: 'center',
+ width: 'full',
+ outline: '0',
+ gap: '1',
+ fontWeight: 'medium',
+ borderRadius: 'var(--accordion-radius)',
+ cursor: 'pointer',
+ _focusVisible: {
+ outline: '2px solid',
+ outlineColor: 'colorPalette.focusRing',
+ },
+ },
+ itemBody: {
+ pt: '0',
+ pb: 'var(--accordion-padding-y)',
+ },
+ itemContent: {
+ overflow: 'hidden',
+ borderRadius: 'var(--accordion-radius)',
+ _open: {
+ animationName: 'expand-height, fade-in',
+ animationDuration: 'moderate',
+ },
+ _closed: {
+ animationName: 'collapse-height, fade-out',
+ animationDuration: 'moderate',
+ },
+ },
+ itemIndicator: {
+ transition: 'rotate 0.2s ease-in-out',
+ transformOrigin: 'center',
+ },
+ },
+
+ variants: {
+ noAnimation: {
+ 'true': {
+ itemContent: {
+ _open: {
+ animationName: 'none',
+ },
+ _closed: {
+ animationName: 'none',
+ },
+ },
+ itemIndicator: {
+ transition: 'none',
+ },
+ },
+ },
+ variant: {
+ outline: {
+ item: {
+ borderBottomWidth: '1px',
+ },
+ itemIndicator: {
+ color: 'gray.500',
+ },
+ },
+ faq: {
+ item: {
+ borderBottomWidth: '1px',
+ },
+ itemTrigger: {
+ textStyle: 'heading.md',
+ },
+ itemIndicator: {
+ color: 'link.primary',
+ _groupHover: {
+ color: 'link.primary.hover',
+ },
+ },
+ },
+ },
+
+ size: {
+ sm: {
+ root: {
+ '--accordion-padding-x': '0',
+ '--accordion-padding-y': 'spacing.2',
+ },
+ itemTrigger: {
+ textStyle: 'sm',
+ py: 'var(--accordion-padding-y)',
+ },
+ itemIndicator: {
+ boxSize: '5',
+ },
+ },
+ md: {
+ root: {
+ '--accordion-padding-x': '0',
+ '--accordion-padding-y': 'spacing.3',
+ },
+ itemTrigger: {
+ textStyle: 'md',
+ py: 'var(--accordion-padding-y)',
+ },
+ itemIndicator: {
+ boxSize: '5',
+ },
+ },
+ },
+ },
+
+ compoundVariants: [
+ {
+ variant: 'faq',
+ size: 'md',
+ css: {
+ itemIndicator: {
+ boxSize: '14px',
+ margin: '5px',
+ },
+ itemBody: {
+ paddingLeft: '36px',
+ },
+ itemTrigger: {
+ gap: '3',
+ },
+ },
+ },
+ ],
+
+ defaultVariants: {
+ size: 'md',
+ variant: 'outline',
+ },
+});
diff --git a/toolkit/theme/recipes/alert.recipe.ts b/toolkit/theme/recipes/alert.recipe.ts
new file mode 100644
index 0000000000..955866d07f
--- /dev/null
+++ b/toolkit/theme/recipes/alert.recipe.ts
@@ -0,0 +1,135 @@
+import { defineSlotRecipe } from '@chakra-ui/react';
+
+export const recipe = defineSlotRecipe({
+ slots: [ 'root', 'title', 'description', 'indicator', 'content' ],
+
+ base: {
+ root: {
+ width: 'full',
+ display: 'flex',
+ alignItems: 'flex-start',
+ position: 'relative',
+ borderRadius: 'base',
+ color: 'alert.fg',
+ },
+ title: {
+ fontWeight: '600',
+ },
+ description: {
+ display: 'inline',
+ },
+ indicator: {
+ display: 'inline-flex',
+ alignItems: 'center',
+ justifyContent: 'center',
+ flexShrink: '0',
+ width: '5',
+ height: '5',
+ _icon: { boxSize: 'full' },
+ color: 'alert.fg',
+ },
+ content: {
+ display: 'flex',
+ flex: '1',
+ },
+ },
+
+ variants: {
+ status: {
+ info: {},
+ warning: {},
+ warning_table: {},
+ success: {},
+ error: {},
+ },
+
+ variant: {
+ subtle: {
+ root: {
+ color: 'alert.fg',
+ },
+ },
+ },
+
+ inline: {
+ 'true': {
+ root: {
+ alignItems: 'flex-start',
+ },
+ content: {
+ display: 'inline-flex',
+ flexDirection: 'row',
+ alignItems: 'center',
+ },
+ },
+ 'false': {
+ content: {
+ display: 'flex',
+ flexDirection: 'column',
+ },
+ },
+ },
+
+ size: {
+ md: {
+ root: {
+ gap: '2',
+ px: '3',
+ py: '2',
+ textStyle: 'md',
+ },
+ indicator: {
+ boxSize: '5',
+ my: '2px',
+ },
+ },
+ },
+ },
+
+ compoundVariants: [
+ {
+ status: 'info',
+ variant: 'subtle',
+ css: {
+ root: {
+ bg: 'alert.bg.info',
+ },
+ },
+ },
+ {
+ status: 'warning',
+ variant: 'subtle',
+ css: {
+ root: { bg: 'alert.bg.warning' },
+ },
+ },
+ {
+ status: 'warning_table',
+ variant: 'subtle',
+ css: {
+ root: { bg: 'alert.bg.warning_table' },
+ },
+ },
+ {
+ status: 'success',
+ variant: 'subtle',
+ css: {
+ root: { bg: 'alert.bg.success' },
+ },
+ },
+ {
+ status: 'error',
+ variant: 'subtle',
+ css: {
+ root: { bg: 'alert.bg.error' },
+ },
+ },
+ ],
+
+ defaultVariants: {
+ status: 'info',
+ size: 'md',
+ inline: true,
+ variant: 'subtle',
+ },
+});
diff --git a/toolkit/theme/recipes/badge.recipe.ts b/toolkit/theme/recipes/badge.recipe.ts
new file mode 100644
index 0000000000..c9cebae5e9
--- /dev/null
+++ b/toolkit/theme/recipes/badge.recipe.ts
@@ -0,0 +1,83 @@
+import { defineRecipe } from '@chakra-ui/react';
+
+export const recipe = defineRecipe({
+ base: {
+ display: 'inline-flex',
+ alignItems: 'center',
+ borderRadius: 'sm',
+ gap: '1',
+ fontWeight: '500',
+ width: 'fit-content',
+ maxWidth: '100%',
+ whiteSpace: 'nowrap',
+ fontVariantNumeric: 'normal',
+ userSelect: 'none',
+ _loading: {
+ borderRadius: 'sm',
+ },
+ },
+ variants: {
+ variant: {
+ subtle: {},
+ },
+ colorPalette: {
+ gray: {
+ bg: 'badge.gray.bg',
+ color: 'badge.gray.fg',
+ },
+ green: {
+ bg: 'badge.green.bg',
+ color: 'badge.green.fg',
+ },
+ red: {
+ bg: 'badge.red.bg',
+ color: 'badge.red.fg',
+ },
+ purple: {
+ bg: 'badge.purple.bg',
+ color: 'badge.purple.fg',
+ },
+ orange: {
+ bg: 'badge.orange.bg',
+ color: 'badge.orange.fg',
+ },
+ blue: {
+ bg: 'badge.blue.bg',
+ color: 'badge.blue.fg',
+ },
+ yellow: {
+ bg: 'badge.yellow.bg',
+ color: 'badge.yellow.fg',
+ },
+ teal: {
+ bg: 'badge.teal.bg',
+ color: 'badge.teal.fg',
+ },
+ cyan: {
+ bg: 'badge.cyan.bg',
+ color: 'badge.cyan.fg',
+ },
+ purple_alt: {
+ bg: 'badge.purple_alt.bg',
+ color: 'badge.purple_alt.fg',
+ },
+ blue_alt: {
+ bg: 'badge.blue_alt.bg',
+ color: 'badge.blue_alt.fg',
+ },
+ },
+ size: {
+ md: {
+ textStyle: 'sm',
+ px: '1',
+ py: '0.5',
+ minH: '6',
+ },
+ },
+ },
+ defaultVariants: {
+ variant: 'subtle',
+ colorPalette: 'gray',
+ size: 'md',
+ },
+});
diff --git a/toolkit/theme/recipes/button.recipe.ts b/toolkit/theme/recipes/button.recipe.ts
new file mode 100644
index 0000000000..24bfb1d016
--- /dev/null
+++ b/toolkit/theme/recipes/button.recipe.ts
@@ -0,0 +1,285 @@
+import { defineRecipe } from '@chakra-ui/react';
+
+export const recipe = defineRecipe({
+ base: {
+ display: 'flex',
+ gap: 0,
+ fontWeight: 600,
+ overflow: 'hidden',
+ _disabled: {
+ opacity: 'control.disabled',
+ },
+ },
+ variants: {
+ variant: {
+ solid: {
+ bg: 'blue.600',
+ color: 'white',
+ _hover: {
+ bg: 'blue.400',
+ },
+ _loading: {
+ opacity: 1,
+ '& .chakra-spinner': {
+ borderColor: 'gray.200',
+ borderBottomColor: 'spinner.track',
+ borderInlineStartColor: 'spinner.track',
+ },
+ },
+ _expanded: {
+ bg: 'blue.400',
+ },
+ },
+ outline: {
+ borderWidth: '2px',
+ borderStyle: 'solid',
+ bg: 'transparent',
+ color: 'button.outline.fg',
+ borderColor: 'button.outline.fg',
+ _hover: {
+ bg: 'transparent',
+ color: 'blue.400',
+ borderColor: 'blue.400',
+ },
+ _loading: {
+ opacity: 1,
+ '& .chakra-spinner': {
+ borderColor: 'button.outline.fg',
+ borderBottomColor: 'spinner.track',
+ borderInlineStartColor: 'spinner.track',
+ },
+ },
+ },
+ dropdown: {
+ borderWidth: '2px',
+ borderStyle: 'solid',
+ bg: 'transparent',
+ color: 'button.dropdown.fg',
+ borderColor: 'button.dropdown.border',
+ _hover: {
+ bg: 'transparent',
+ color: 'blue.400',
+ borderColor: 'blue.400',
+ },
+ _loading: {
+ opacity: 1,
+ '& .chakra-spinner': {
+ borderColor: 'blue.500',
+ borderBottomColor: 'spinner.track',
+ borderInlineStartColor: 'spinner.track',
+ },
+ },
+ // When the dropdown is open, the button should be active
+ _expanded: {
+ bg: 'transparent',
+ color: 'blue.400',
+ borderColor: 'blue.400',
+ },
+ // We have a special state for this button variant that serves as a popover trigger.
+ // When any items (filters) are selected in the popover, the button should change its background and text color.
+ // The last CSS selector is for redefining styles for the TabList component.
+ _selected: {
+ bg: 'button.dropdown.bg.selected',
+ color: 'button.dropdown.fg.selected',
+ borderColor: 'transparent',
+ _hover: {
+ bg: 'button.dropdown.bg.selected',
+ color: 'button.dropdown.fg.selected',
+ borderColor: 'transparent',
+ },
+ },
+ },
+ header: {
+ bg: 'transparent',
+ color: 'button.header.fg',
+ borderColor: 'button.header.border',
+ borderWidth: '2px',
+ borderStyle: 'solid',
+ _hover: {
+ bg: 'transparent',
+ color: 'blue.400',
+ borderColor: 'blue.400',
+ },
+ _loading: {
+ opacity: 1,
+ '& .chakra-spinner': {
+ borderColor: 'blue.500',
+ borderBottomColor: 'spinner.track',
+ borderInlineStartColor: 'spinner.track',
+ },
+ },
+ _selected: {
+ bg: 'button.header.bg.selected',
+ color: 'button.header.fg.selected',
+ borderColor: 'transparent',
+ borderWidth: '0px',
+ _hover: {
+ bg: 'button.header.bg.selected',
+ color: 'button.header.fg.selected',
+ },
+ _highlighted: {
+ bg: 'button.header.bg.highlighted',
+ color: 'button.header.fg.highlighted',
+ borderColor: 'transparent',
+ borderWidth: '0px',
+ _hover: {
+ bg: 'button.header.bg.highlighted',
+ color: 'button.header.fg.highlighted',
+ },
+ },
+ },
+ },
+ hero: {
+ bg: 'button.hero.bg',
+ color: 'button.hero.fg',
+ _loading: {
+ opacity: 1,
+ '& .chakra-spinner': {
+ borderColor: 'button.hero.fg',
+ borderBottomColor: 'spinner.track',
+ borderInlineStartColor: 'spinner.track',
+ },
+ },
+ _hover: {
+ bg: 'button.hero.bg.hover',
+ color: 'button.hero.fg.hover',
+ },
+ _selected: {
+ bg: 'button.hero.bg.selected',
+ color: 'button.hero.fg.selected',
+ _hover: {
+ bg: 'button.hero.bg.selected',
+ color: 'button.hero.fg.selected',
+ },
+ },
+ },
+ segmented: {
+ bg: 'transparent',
+ color: 'button.segmented.fg',
+ borderColor: 'button.segmented.border',
+ borderWidth: '2px',
+ borderStyle: 'solid',
+ borderRadius: 'none',
+ _hover: {
+ color: 'link.primary.hover',
+ },
+ _selected: {
+ bg: 'button.segmented.border',
+ color: 'button.segmented.fg.selected',
+ _hover: {
+ bg: 'button.segmented.border',
+ color: 'button.segmented.fg.selected',
+ },
+ },
+ _notFirst: {
+ borderLeftWidth: '0',
+ },
+ _first: {
+ borderTopLeftRadius: 'base',
+ borderBottomLeftRadius: 'base',
+ },
+ _last: {
+ borderTopRightRadius: 'base',
+ borderBottomRightRadius: 'base',
+ },
+ },
+ plain: {
+ bg: 'transparent',
+ color: 'inherit',
+ border: 'none',
+ _hover: {
+ bg: 'transparent',
+ },
+ },
+ subtle: {
+ bg: 'button.subtle.bg',
+ color: 'button.subtle.fg',
+ _hover: {
+ bg: 'button.subtle.bg',
+ color: 'link.primary.hover',
+ },
+ _disabled: {
+ bg: 'button.subtle.bg',
+ color: 'button.subtle.fg',
+ },
+ },
+ link: {
+ bg: 'transparent',
+ color: 'link.primary',
+ border: 'none',
+ fontWeight: '400',
+ px: 0,
+ h: 'auto',
+ _hover: {
+ bg: 'transparent',
+ color: 'link.primary.hover',
+ },
+ _disabled: {
+ color: 'text.secondary',
+ },
+ },
+ icon_secondary: {
+ bg: 'transparent',
+ color: 'button.icon_secondary.fg',
+ border: 'none',
+ _hover: {
+ color: 'link.primary.hover',
+ },
+ _selected: {
+ bg: 'button.icon_secondary.bg.selected',
+ color: 'button.icon_secondary.fg.selected',
+ _hover: {
+ bg: 'button.icon_secondary.bg.selected',
+ color: 'button.icon_secondary.fg.selected',
+ },
+ },
+ _expanded: {
+ color: 'link.primary.hover',
+ },
+ },
+ },
+ size: {
+ '2xs': {
+ px: 2,
+ h: 5,
+ minW: 5,
+ textStyle: 'xs',
+ borderRadius: 'sm',
+ gap: 1,
+ _icon: { boxSize: 'auto' },
+ },
+ xs: {
+ px: 2,
+ h: 6,
+ minW: 6,
+ textStyle: 'sm',
+ borderRadius: 'sm',
+ gap: 1,
+ _icon: { boxSize: 'auto' },
+ },
+ sm: {
+ px: 3,
+ h: 8,
+ minW: 8,
+ textStyle: 'sm',
+ borderRadius: 'base',
+ gap: 1,
+ _icon: { boxSize: 'auto' },
+ },
+ md: {
+ px: 3,
+ h: 10,
+ minW: 10,
+ textStyle: 'md',
+ borderRadius: 'base',
+ gap: 2,
+ _icon: { boxSize: 'auto' },
+ '& .chakra-spinner': { '--spinner-size': '20px' },
+ },
+ },
+ },
+ defaultVariants: {
+ size: 'md',
+ variant: 'solid',
+ },
+});
diff --git a/toolkit/theme/recipes/checkbox.recipe.ts b/toolkit/theme/recipes/checkbox.recipe.ts
new file mode 100644
index 0000000000..9b9ef6a639
--- /dev/null
+++ b/toolkit/theme/recipes/checkbox.recipe.ts
@@ -0,0 +1,56 @@
+import { defineSlotRecipe } from '@chakra-ui/react';
+
+import { recipe as checkmarkRecipe } from './checkmark.recipe';
+
+export const recipe = defineSlotRecipe({
+ slots: [ 'root', 'control', 'label' ],
+ className: 'chakra-checkbox',
+ base: {
+ root: {
+ display: 'inline-flex',
+ gap: '2',
+ alignItems: 'center',
+ verticalAlign: 'top',
+ position: 'relative',
+ cursor: 'pointer',
+ _disabled: {
+ cursor: 'disabled',
+ },
+ _readOnly: {
+ cursor: 'default',
+ },
+ },
+
+ control: checkmarkRecipe.base,
+
+ label: {
+ fontWeight: 'normal',
+ userSelect: 'none',
+ flexGrow: 1,
+ _disabled: {
+ opacity: 'control.disabled',
+ },
+ },
+ },
+
+ variants: {
+ size: {
+ md: {
+ root: { gap: '2' },
+ label: { textStyle: 'md' },
+ control: checkmarkRecipe.variants?.size?.md,
+ },
+ },
+
+ variant: {
+ solid: {
+ control: checkmarkRecipe.variants?.variant?.solid,
+ },
+ },
+ },
+
+ defaultVariants: {
+ variant: 'solid',
+ size: 'md',
+ },
+});
diff --git a/toolkit/theme/recipes/checkmark.recipe.ts b/toolkit/theme/recipes/checkmark.recipe.ts
new file mode 100644
index 0000000000..00f3e6c105
--- /dev/null
+++ b/toolkit/theme/recipes/checkmark.recipe.ts
@@ -0,0 +1,73 @@
+import { defineRecipe } from '@chakra-ui/react';
+
+export const recipe = defineRecipe({
+ className: 'chakra-checkmark',
+ base: {
+ display: 'inline-flex',
+ alignItems: 'center',
+ justifyContent: 'center',
+ flexShrink: '0',
+ color: 'white',
+ borderWidth: '2px',
+ borderColor: 'transparent',
+ focusVisibleRing: 'outside',
+ _icon: {
+ boxSize: 'full',
+ },
+ _disabled: {
+ opacity: 'control.disabled',
+ },
+ },
+ variants: {
+ size: {
+ xs: {
+ boxSize: '3',
+ borderRadius: '2px',
+ },
+ sm: {
+ boxSize: '4',
+ borderRadius: '2px',
+ },
+ md: {
+ boxSize: '5',
+ borderRadius: 'sm',
+ },
+ },
+
+ variant: {
+ solid: {
+ borderColor: 'checkbox.control.border',
+ _hover: {
+ borderColor: 'checkbox.control.border.hover',
+ },
+ _readOnly: {
+ borderColor: 'checkbox.control.border.readOnly',
+ _hover: {
+ borderColor: 'checkbox.control.border.readOnly',
+ },
+ '&:is([data-state=checked], [data-state=indeterminate])': {
+ bg: 'checkbox.control.border.readOnly',
+ color: 'gray.500',
+ _hover: {
+ bg: 'checkbox.control.border.readOnly',
+ },
+ },
+ },
+ '&:is([data-state=checked], [data-state=indeterminate])': {
+ bg: 'blue.500',
+ color: 'white',
+ borderColor: 'blue.500',
+ _hover: {
+ bg: 'blue.400',
+ borderColor: 'blue.400',
+ },
+ },
+ },
+ },
+ },
+
+ defaultVariants: {
+ variant: 'solid',
+ size: 'md',
+ },
+});
diff --git a/toolkit/theme/recipes/close-button.recipe.ts b/toolkit/theme/recipes/close-button.recipe.ts
new file mode 100644
index 0000000000..8bb205ca78
--- /dev/null
+++ b/toolkit/theme/recipes/close-button.recipe.ts
@@ -0,0 +1,34 @@
+import { defineRecipe } from '@chakra-ui/react';
+
+export const recipe = defineRecipe({
+ base: {
+ display: 'flex',
+ gap: 0,
+ borderRadius: 'sm',
+ overflow: 'hidden',
+ _disabled: {
+ opacity: 'control.disabled',
+ },
+ minWidth: 'auto',
+ },
+ variants: {
+ visual: {
+ plain: {
+ bg: 'transparent',
+ color: 'closeButton.fg',
+ border: 'none',
+ _hover: {
+ bg: 'transparent',
+ color: 'link.primary.hover',
+ },
+ },
+ },
+ size: {
+ md: { boxSize: 5 },
+ },
+ },
+ defaultVariants: {
+ size: 'md',
+ visual: 'plain',
+ },
+});
diff --git a/toolkit/theme/recipes/dialog.recipe.ts b/toolkit/theme/recipes/dialog.recipe.ts
new file mode 100644
index 0000000000..5747070737
--- /dev/null
+++ b/toolkit/theme/recipes/dialog.recipe.ts
@@ -0,0 +1,213 @@
+import { defineSlotRecipe } from '@chakra-ui/react';
+
+export const recipe = defineSlotRecipe({
+ slots: [ 'backdrop', 'positioner', 'content', 'header', 'body', 'footer', 'title', 'description' ],
+ base: {
+ backdrop: {
+ bg: 'blackAlpha.800',
+ pos: 'fixed',
+ left: 0,
+ top: 0,
+ w: '100vw',
+ h: '100dvh',
+ zIndex: 'modal',
+ _open: {
+ animationName: 'fade-in',
+ animationDuration: 'slow',
+ },
+ _closed: {
+ animationName: 'fade-out',
+ animationDuration: 'moderate',
+ },
+ },
+ positioner: {
+ display: 'flex',
+ width: '100vw',
+ height: '100dvh',
+ position: 'fixed',
+ left: 0,
+ top: 0,
+ '--dialog-z-index': 'zIndex.modal',
+ zIndex: 'calc(var(--dialog-z-index) + var(--layer-index, 0))',
+ justifyContent: 'center',
+ overscrollBehaviorY: 'none',
+ },
+ content: {
+ display: 'flex',
+ flexDirection: 'column',
+ position: 'relative',
+ width: '100%',
+ padding: 6,
+ outline: 0,
+ textStyle: 'md',
+ my: 'var(--dialog-margin, var(--dialog-base-margin))',
+ '--dialog-z-index': 'zIndex.modal',
+ zIndex: 'calc(var(--dialog-z-index) + var(--layer-index, 0))',
+ bg: 'dialog.bg',
+ color: 'dialog.fg',
+ boxShadow: 'size.lg',
+ borderRadius: 'xl',
+ _open: {
+ animationDuration: 'moderate',
+ },
+ _closed: {
+ animationDuration: 'faster',
+ },
+ },
+ header: {
+ flex: 0,
+ p: 0,
+ mb: 2,
+ display: 'flex',
+ alignItems: 'center',
+ columnGap: 2,
+ minH: '40px',
+ },
+ body: {
+ flex: '1',
+ p: 0,
+ },
+ footer: {
+ display: 'flex',
+ alignItems: 'center',
+ justifyContent: 'flex-start',
+ gap: '6',
+ p: '0',
+ mt: '6',
+ },
+ title: {
+ textStyle: 'heading.lg',
+ fontWeight: '500',
+ },
+ description: {
+ color: 'dialog.fg',
+ },
+ },
+
+ variants: {
+ placement: {
+ center: {
+ positioner: {
+ alignItems: 'center',
+ },
+ content: {
+ '--dialog-base-margin': 'auto',
+ mx: 'auto',
+ },
+ },
+ top: {
+ positioner: {
+ alignItems: 'flex-start',
+ },
+ content: {
+ '--dialog-base-margin': 'spacing.16',
+ mx: 'auto',
+ },
+ },
+ bottom: {
+ positioner: {
+ alignItems: 'flex-end',
+ },
+ content: {
+ '--dialog-base-margin': 'spacing.16',
+ mx: 'auto',
+ },
+ },
+ },
+
+ scrollBehavior: {
+ inside: {
+ positioner: {
+ overflow: 'hidden',
+ },
+ content: {
+ // source code has minH: 'auto', but I am not sure why
+ // anyway it will override the minH from the "full" size variant
+ // minH: 'auto',
+ maxH: 'calc(100% - 7.5rem)',
+ },
+ body: {
+ overflow: 'auto',
+ },
+ },
+ outside: {
+ positioner: {
+ overflow: 'auto',
+ pointerEvents: 'auto',
+ },
+ },
+ },
+
+ size: {
+ sm: {
+ content: {
+ maxW: '400px',
+ },
+ },
+ md: {
+ content: {
+ maxW: '728px',
+ },
+ },
+ cover: {
+ positioner: {
+ padding: '10',
+ },
+ content: {
+ width: '100%',
+ height: '100%',
+ '--dialog-margin': '0',
+ },
+ },
+ full: {
+ content: {
+ maxW: '100vw',
+ minH: '100vh',
+ '--dialog-margin': '0',
+ borderRadius: '0',
+ },
+ },
+ },
+
+ motionPreset: {
+ scale: {
+ content: {
+ _open: { animationName: 'scale-in, fade-in' },
+ _closed: { animationName: 'scale-out, fade-out' },
+ },
+ },
+ 'slide-in-bottom': {
+ content: {
+ _open: { animationName: 'slide-from-bottom, fade-in' },
+ _closed: { animationName: 'slide-to-bottom, fade-out' },
+ },
+ },
+ 'slide-in-top': {
+ content: {
+ _open: { animationName: 'slide-from-top, fade-in' },
+ _closed: { animationName: 'slide-to-top, fade-out' },
+ },
+ },
+ 'slide-in-left': {
+ content: {
+ _open: { animationName: 'slide-from-left, fade-in' },
+ _closed: { animationName: 'slide-to-left, fade-out' },
+ },
+ },
+ 'slide-in-right': {
+ content: {
+ _open: { animationName: 'slide-from-right, fade-in' },
+ _closed: { animationName: 'slide-to-right, fade-out' },
+ },
+ },
+ none: {},
+ },
+ },
+
+ defaultVariants: {
+ size: 'md',
+ scrollBehavior: 'inside',
+ placement: 'center',
+ motionPreset: 'scale',
+ },
+});
diff --git a/toolkit/theme/recipes/drawer.recipe.ts b/toolkit/theme/recipes/drawer.recipe.ts
new file mode 100644
index 0000000000..61bdf6a34b
--- /dev/null
+++ b/toolkit/theme/recipes/drawer.recipe.ts
@@ -0,0 +1,173 @@
+import { defineSlotRecipe } from '@chakra-ui/react';
+
+export const recipe = defineSlotRecipe({
+ slots: [ 'root', 'backdrop', 'trigger', 'content', 'header', 'body', 'footer', 'title', 'description', 'positioner' ],
+ className: 'chakra-drawer',
+ base: {
+ backdrop: {
+ bg: 'blackAlpha.800',
+ pos: 'fixed',
+ insetInlineStart: 0,
+ top: 0,
+ w: '100vw',
+ h: '100dvh',
+ zIndex: 'modal',
+ _open: {
+ animationName: 'fade-in',
+ animationDuration: 'slow',
+ },
+ _closed: {
+ animationName: 'fade-out',
+ animationDuration: 'moderate',
+ },
+ },
+ positioner: {
+ display: 'flex',
+ width: '100vw',
+ height: '100dvh',
+ position: 'fixed',
+ insetInlineStart: 0,
+ top: 0,
+ zIndex: 'modal',
+ overscrollBehaviorY: 'none',
+ },
+ content: {
+ display: 'flex',
+ flexDirection: 'column',
+ position: 'relative',
+ width: '100%',
+ outline: 0,
+ zIndex: 'modal',
+ textStyle: 'sm',
+ maxH: '100dvh',
+ color: 'inherit',
+ bg: 'drawer.bg',
+ boxShadow: 'drawer',
+ _open: {
+ animationDuration: 'slowest',
+ animationTimingFunction: 'ease-in-smooth',
+ },
+ _closed: {
+ animationDuration: 'slower',
+ animationTimingFunction: 'ease-in-smooth',
+ },
+ },
+ header: {
+ flex: 0,
+ px: '6',
+ pt: '6',
+ pb: '4',
+ },
+ body: {
+ p: '6',
+ flex: '1',
+ overflow: 'auto',
+ },
+ footer: {
+ display: 'flex',
+ alignItems: 'center',
+ justifyContent: 'flex-end',
+ gap: '3',
+ px: '6',
+ pt: '2',
+ pb: '4',
+ },
+ title: {
+ textStyle: 'lg',
+ fontWeight: 'semibold',
+ },
+ description: {
+ color: 'fg.muted',
+ },
+ },
+
+ variants: {
+ size: {
+ md: {
+ content: {
+ maxW: '300px',
+ },
+ },
+ },
+
+ placement: {
+ start: {
+ positioner: {
+ justifyContent: 'flex-start',
+ },
+ content: {
+ _open: {
+ animationName: {
+ base: 'slide-from-left-full, fade-in',
+ _rtl: 'slide-from-right-full, fade-in',
+ },
+ },
+ _closed: {
+ animationName: {
+ base: 'slide-to-left-full, fade-out',
+ _rtl: 'slide-to-right-full, fade-out',
+ },
+ },
+ },
+ },
+
+ end: {
+ positioner: {
+ justifyContent: 'flex-end',
+ },
+ content: {
+ _open: {
+ animationName: {
+ base: 'slide-from-right-full, fade-in',
+ _rtl: 'slide-from-left-full, fade-in',
+ },
+ },
+ _closed: {
+ animationName: {
+ base: 'slide-to-right-full, fade-out',
+ _rtl: 'slide-to-right-full, fade-out',
+ },
+ },
+ },
+ },
+
+ top: {
+ positioner: {
+ alignItems: 'flex-start',
+ },
+ content: {
+ maxW: '100%',
+ _open: { animationName: 'slide-from-top-full, fade-in' },
+ _closed: { animationName: 'slide-to-top-full, fade-out' },
+ },
+ },
+
+ bottom: {
+ positioner: {
+ alignItems: 'flex-end',
+ },
+ content: {
+ maxW: '100%',
+ _open: { animationName: 'slide-from-bottom-full, fade-in' },
+ _closed: { animationName: 'slide-to-bottom-full, fade-out' },
+ },
+ },
+ },
+
+ contained: {
+ 'true': {
+ positioner: {
+ padding: '4',
+ },
+ content: {
+ borderRadius: 'l3',
+ },
+ },
+ },
+ },
+
+ defaultVariants: {
+ size: 'md',
+ placement: 'end',
+ },
+});
diff --git a/toolkit/theme/recipes/field.recipe.ts b/toolkit/theme/recipes/field.recipe.ts
new file mode 100644
index 0000000000..7ac89a088b
--- /dev/null
+++ b/toolkit/theme/recipes/field.recipe.ts
@@ -0,0 +1,175 @@
+import { defineSlotRecipe } from '@chakra-ui/react';
+
+export const recipe = defineSlotRecipe({
+ className: 'chakra-field',
+ slots: [ 'root', 'label', 'requiredIndicator', 'errorText', 'helperText' ],
+ base: {
+ requiredIndicator: {
+ color: 'inherit',
+ lineHeight: 'inherit',
+ },
+ root: {
+ display: 'flex',
+ width: '100%',
+ position: 'relative',
+ gap: '1',
+ },
+ label: {
+ display: 'flex',
+ alignItems: 'center',
+ textAlign: 'start',
+ textStyle: 'sm',
+ fontWeight: '500',
+ gap: '0',
+ userSelect: 'none',
+ zIndex: '1',
+ _disabled: {
+ opacity: 'control.disabled',
+ },
+ _invalid: {
+ color: 'input.fg.error',
+ },
+ },
+ errorText: {
+ display: 'inline-flex',
+ alignItems: 'center',
+ fontWeight: 'medium',
+ gap: '1',
+ color: 'input.fg.error',
+ textStyle: 'sm',
+ },
+ helperText: {
+ color: 'fg.muted',
+ textStyle: 'sm',
+ },
+ },
+
+ variants: {
+ floating: {
+ 'true': {
+ label: {
+ pos: 'absolute',
+ bg: 'bg',
+ top: '2px',
+ left: '2px',
+ color: 'input.placeholder',
+ width: 'calc(100% - 4px)',
+ borderRadius: 'base',
+ pointerEvents: 'none',
+ transformOrigin: 'top left',
+ transitionProperty: 'font-size, line-height, padding, background-color',
+ transitionDuration: 'fast',
+ transitionTimingFunction: 'ease',
+ },
+ },
+ },
+ size: {
+ sm: {
+ label: {
+ fontSize: 'sm',
+ },
+ },
+ md: {
+ label: {
+ fontSize: 'md',
+ },
+ },
+ lg: {
+ label: {
+ fontSize: 'md',
+ },
+ },
+ // special size for textarea
+ '2xl': {
+ label: {
+ fontSize: 'md',
+ },
+ },
+ },
+ orientation: {
+ vertical: {
+ root: {
+ flexDirection: 'column',
+ alignItems: 'flex-start',
+ },
+ },
+ horizontal: {
+ root: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ justifyContent: 'space-between',
+ },
+ label: {
+ flex: '0 0 var(--field-label-width, 80px)',
+ },
+ },
+ },
+ },
+
+ compoundVariants: [
+ {
+ size: 'lg',
+ floating: true,
+ css: {
+ label: {
+ whiteSpace: 'nowrap',
+ overflow: 'hidden',
+ textOverflow: 'ellipsis',
+ padding: '10px 16px 0px 16px',
+ textStyle: 'xs',
+ _peerPlaceholderShown: {
+ padding: '16px',
+ textStyle: 'md',
+ },
+ _peerFocusVisible: {
+ padding: '10px 16px 0px 16px',
+ textStyle: 'xs',
+ },
+ _readOnly: {
+ bg: 'input.bg.readOnly',
+ },
+ },
+ errorText: {
+ fontSize: 'inherit',
+ lineHeight: 'inherit',
+ },
+ },
+ },
+ {
+ size: '2xl',
+ floating: true,
+ css: {
+ label: {
+ whiteSpace: 'nowrap',
+ overflow: 'hidden',
+ textOverflow: 'ellipsis',
+ // 20px = scrollbar width
+ // 4px = border width
+ width: 'calc(100% - 4px - 20px)',
+ padding: '16px 16px 0px 16px',
+ textStyle: 'xs',
+ borderTopRightRadius: '0px',
+ borderBottomRightRadius: '0px',
+ _peerPlaceholderShown: {
+ textStyle: 'md',
+ },
+ _peerFocusVisible: {
+ textStyle: 'xs',
+ },
+ _readOnly: {
+ bg: 'input.bg.readOnly',
+ },
+ },
+ errorText: {
+ fontSize: 'inherit',
+ lineHeight: 'inherit',
+ },
+ },
+ },
+ ],
+
+ defaultVariants: {
+ floating: false,
+ orientation: 'vertical',
+ },
+});
diff --git a/toolkit/theme/recipes/index.ts b/toolkit/theme/recipes/index.ts
new file mode 100644
index 0000000000..82dc25d60c
--- /dev/null
+++ b/toolkit/theme/recipes/index.ts
@@ -0,0 +1,68 @@
+import { recipe as accordion } from './accordion.recipe';
+import { recipe as alert } from './alert.recipe';
+import { recipe as badge } from './badge.recipe';
+import { recipe as button } from './button.recipe';
+import { recipe as checkbox } from './checkbox.recipe';
+import { recipe as checkmark } from './checkmark.recipe';
+import { recipe as closeButton } from './close-button.recipe';
+import { recipe as dialog } from './dialog.recipe';
+import { recipe as drawer } from './drawer.recipe';
+import { recipe as field } from './field.recipe';
+import { recipe as input } from './input.recipe';
+import { recipe as link } from './link.recipe';
+import { recipe as list } from './list.recipe';
+import { recipe as menu } from './menu.recipe';
+import { recipe as pinInput } from './pin-input.recipe';
+import { recipe as popover } from './popover.recipe';
+import { recipe as progressCircle } from './progress-circle.recipe';
+import { recipe as radioGroup } from './radio-group.recipe';
+import { recipe as radiomark } from './radiomark.recipe';
+import { recipe as ratingGroup } from './rating-group.recipe';
+import { recipe as select } from './select.recipe';
+import { recipe as skeleton } from './skeleton.recipe';
+import { recipe as spinner } from './spinner.recipe';
+import { recipe as stat } from './stat.recipe';
+import { recipe as switchRecipe } from './switch.recipe';
+import { recipe as table } from './table.recipe';
+import { recipe as tabs } from './tabs.recipe';
+import { recipe as tag } from './tag.recipe';
+import { recipe as textarea } from './textarea.recipe';
+import { recipe as toast } from './toast.recipe';
+import { recipe as tooltip } from './tooltip.recipe';
+
+export const recipes = {
+ badge,
+ button,
+ checkmark,
+ closeButton,
+ input,
+ link,
+ radiomark,
+ skeleton,
+ spinner,
+ textarea,
+};
+
+export const slotRecipes = {
+ accordion,
+ alert,
+ checkbox,
+ dialog,
+ drawer,
+ field,
+ list,
+ menu,
+ pinInput,
+ popover,
+ progressCircle,
+ radioGroup,
+ ratingGroup,
+ select,
+ stat,
+ 'switch': switchRecipe,
+ table,
+ tabs,
+ tag,
+ toast,
+ tooltip,
+};
diff --git a/toolkit/theme/recipes/input.recipe.ts b/toolkit/theme/recipes/input.recipe.ts
new file mode 100644
index 0000000000..b5af17ef30
--- /dev/null
+++ b/toolkit/theme/recipes/input.recipe.ts
@@ -0,0 +1,119 @@
+import { defineRecipe } from '@chakra-ui/react';
+
+export const recipe = defineRecipe({
+ base: {
+ width: '100%',
+ minWidth: '0',
+ outline: '0',
+ position: 'relative',
+ appearance: 'textfield',
+ textAlign: 'start',
+ borderRadius: 'base',
+ height: 'var(--input-height)',
+ minW: 'var(--input-height)',
+ color: 'input.fg',
+ '--focus-color': 'colors.border.error',
+ '--error-color': 'colors.border.error',
+ _invalid: {
+ focusRingColor: 'var(--error-color)',
+ borderColor: 'var(--error-color)',
+ },
+ _readOnly: {
+ overflow: 'hidden',
+ textOverflow: 'ellipsis',
+ whiteSpace: 'nowrap',
+ },
+ },
+
+ variants: {
+ size: {
+ sm: {
+ textStyle: 'sm',
+ px: '2',
+ '--input-height': 'sizes.8',
+ },
+ md: {
+ textStyle: 'sm',
+ px: '2',
+ '--input-height': 'sizes.10',
+ },
+ lg: {
+ textStyle: 'md',
+ px: '4',
+ '--input-height': '60px',
+ },
+ },
+
+ variant: {
+ outline: {
+ bg: 'input.bg',
+ borderWidth: '2px',
+ borderColor: 'input.border.filled',
+ focusVisibleRing: 'none',
+ _placeholder: {
+ color: 'input.placeholder',
+ },
+ _placeholderShown: {
+ borderColor: 'input.border',
+ _invalid: {
+ borderColor: 'input.border.error',
+ },
+ },
+ _hover: {
+ borderColor: 'input.border.hover',
+ },
+ _focus: {
+ borderColor: 'input.border.focus',
+ boxShadow: 'size.md',
+ _hover: {
+ borderColor: 'input.border.focus',
+ },
+ },
+ _readOnly: {
+ userSelect: 'all',
+ pointerEvents: 'none',
+ bg: 'input.bg.readOnly',
+ borderColor: 'input.border.readOnly',
+ _focus: {
+ borderColor: 'input.border.readOnly',
+ },
+ _hover: {
+ borderColor: 'input.border.readOnly',
+ },
+ },
+ _disabled: {
+ opacity: 'control.disabled',
+ },
+ _invalid: {
+ borderColor: 'input.border.error',
+ _placeholder: {
+ color: 'input.placeholder.error',
+ },
+ _hover: {
+ borderColor: 'input.border.error',
+ },
+ },
+ },
+ },
+
+ floating: {
+ 'true': {},
+ },
+ },
+
+ compoundVariants: [
+ {
+ size: 'lg',
+ floating: true,
+ css: {
+ padding: '26px 10px 10px 16px',
+ },
+ },
+ ],
+
+ defaultVariants: {
+ size: 'md',
+ variant: 'outline',
+ floating: false,
+ },
+});
diff --git a/toolkit/theme/recipes/link.recipe.ts b/toolkit/theme/recipes/link.recipe.ts
new file mode 100644
index 0000000000..3b5f64c3de
--- /dev/null
+++ b/toolkit/theme/recipes/link.recipe.ts
@@ -0,0 +1,78 @@
+import { defineRecipe } from '@chakra-ui/react';
+
+export const recipe = defineRecipe({
+ base: {
+ gap: 0,
+ _disabled: {
+ cursor: 'not-allowed',
+ },
+ },
+ variants: {
+ variant: {
+ primary: {
+ color: 'link.primary',
+ _hover: {
+ textDecoration: 'none',
+ color: 'link.primary.hover',
+ },
+ },
+ secondary: {
+ color: 'link.secondary',
+ _hover: {
+ textDecoration: 'none',
+ color: 'link.primary.hover',
+ },
+ },
+ subtle: {
+ color: 'link.subtle',
+ _hover: {
+ color: 'link.subtle.hover',
+ textDecorationLine: 'underline',
+ textDecorationColor: 'link.subtle.hover',
+ },
+ },
+ underlaid: {
+ color: 'link.primary',
+ bgColor: 'link.underlaid.bg',
+ px: '8px',
+ py: '6px',
+ borderRadius: 'base',
+ textStyle: 'sm',
+ _hover: {
+ color: 'link.primary.hover',
+ textDecoration: 'none',
+ },
+ },
+ menu: {
+ color: 'link.menu',
+ _hover: {
+ color: 'link.primary.hover',
+ textDecoration: 'none',
+ },
+ },
+ navigation: {
+ color: 'link.navigation.fg',
+ bg: 'transparent',
+ _hover: {
+ color: 'link.navigation.fg.hover',
+ textDecoration: 'none',
+ },
+ _selected: {
+ color: 'link.navigation.fg.selected',
+ bg: 'link.navigation.bg.selected',
+ },
+ _active: {
+ color: 'link.navigation.fg.active',
+ },
+ },
+ plain: {
+ _hover: {
+ textDecoration: 'none',
+ },
+ },
+ },
+ },
+ defaultVariants: {
+ variant: 'primary',
+ },
+});
diff --git a/toolkit/theme/recipes/list.recipe.ts b/toolkit/theme/recipes/list.recipe.ts
new file mode 100644
index 0000000000..d66af9885e
--- /dev/null
+++ b/toolkit/theme/recipes/list.recipe.ts
@@ -0,0 +1,67 @@
+import { defineSlotRecipe } from '@chakra-ui/react';
+
+export const recipe = defineSlotRecipe({
+ slots: [ 'root', 'item', 'indicator' ],
+ base: {
+ root: {
+ display: 'flex',
+ flexDirection: 'column',
+ gap: 'var(--list-gap)',
+ '& :where(ul, ol)': {
+ marginTop: 'var(--list-gap)',
+ },
+ },
+ item: {
+ whiteSpace: 'normal',
+ display: 'list-item',
+ '&::marker': {
+ color: 'inherit',
+ },
+ },
+ indicator: {
+ marginEnd: '2',
+ minHeight: '1lh',
+ flexShrink: 0,
+ display: 'inline-block',
+ verticalAlign: 'middle',
+ },
+ },
+
+ variants: {
+ variant: {
+ marker: {
+ root: {
+ listStyle: 'revert',
+ },
+ item: {
+ _marker: {
+ color: 'inherit',
+ },
+ },
+ },
+
+ plain: {
+ item: {
+ alignItems: 'flex-start',
+ display: 'inline-flex',
+ },
+ },
+ },
+
+ align: {
+ center: {
+ item: { alignItems: 'center' },
+ },
+ start: {
+ item: { alignItems: 'flex-start' },
+ },
+ end: {
+ item: { alignItems: 'flex-end' },
+ },
+ },
+ },
+
+ defaultVariants: {
+ variant: 'marker',
+ },
+});
diff --git a/toolkit/theme/recipes/menu.recipe.ts b/toolkit/theme/recipes/menu.recipe.ts
new file mode 100644
index 0000000000..c02d5968e6
--- /dev/null
+++ b/toolkit/theme/recipes/menu.recipe.ts
@@ -0,0 +1,105 @@
+import { defineSlotRecipe } from '@chakra-ui/react';
+
+export const recipe = defineSlotRecipe({
+ slots: [ 'content', 'item', 'itemText', 'itemGroupLabel', 'indicator', 'itemCommand', 'separator' ],
+ base: {
+ content: {
+ outline: 0,
+ bg: 'popover.bg',
+ boxShadow: 'popover',
+ color: 'initial',
+ maxHeight: 'var(--available-height)',
+ '--menu-z-index': 'zIndex.dropdown',
+ zIndex: 'calc(var(--menu-z-index) + var(--layer-index, 0))',
+ borderRadius: 'md',
+ overflow: 'hidden',
+ overflowY: 'auto',
+ _open: {
+ animationStyle: 'slide-fade-in',
+ animationDuration: 'fast',
+ },
+ _closed: {
+ animationStyle: 'slide-fade-out',
+ animationDuration: 'faster',
+ },
+ },
+ item: {
+ textDecoration: 'none',
+ color: 'initial',
+ userSelect: 'none',
+ borderRadius: 'none',
+ width: '100%',
+ display: 'flex',
+ cursor: 'pointer',
+ alignItems: 'center',
+ textAlign: 'start',
+ position: 'relative',
+ flex: '0 0 auto',
+ outline: 0,
+ _disabled: {
+ layerStyle: 'disabled',
+ },
+ },
+ itemText: {
+ flex: '1',
+ },
+ itemGroupLabel: {
+ px: '2',
+ py: '1.5',
+ fontWeight: 'semibold',
+ textStyle: 'sm',
+ },
+ indicator: {
+ display: 'inline-flex',
+ alignItems: 'center',
+ justifyContent: 'center',
+ flexShrink: '0',
+ },
+ itemCommand: {
+ opacity: '0.6',
+ textStyle: 'xs',
+ ms: 'auto',
+ ps: '4',
+ letterSpacing: 'widest',
+ },
+ separator: {
+ height: '1px',
+ bg: 'bg.muted',
+ my: '1',
+ mx: '-1',
+ },
+ },
+
+ variants: {
+ variant: {
+ subtle: {
+ item: {
+ _highlighted: {
+ bg: 'menu.item.bg.highlighted',
+ },
+ },
+ },
+ },
+
+ size: {
+ md: {
+ content: {
+ minW: '150px',
+ py: '2',
+ px: '0',
+ },
+ item: {
+ gap: '2',
+ textStyle: 'md',
+ py: '2',
+ px: '4',
+ },
+ },
+ },
+ },
+
+ defaultVariants: {
+ size: 'md',
+ variant: 'subtle',
+ },
+});
diff --git a/toolkit/theme/recipes/pin-input.recipe.ts b/toolkit/theme/recipes/pin-input.recipe.ts
new file mode 100644
index 0000000000..6a915e4d6b
--- /dev/null
+++ b/toolkit/theme/recipes/pin-input.recipe.ts
@@ -0,0 +1,35 @@
+import { defineSlotRecipe } from '@chakra-ui/react';
+
+import { mapEntries } from '../utils/entries';
+import { recipe as inputRecipe } from './input.recipe';
+
+const { variants } = inputRecipe;
+
+export const recipe = defineSlotRecipe({
+ slots: [ 'input' ],
+ base: {
+ input: {
+ ...inputRecipe.base,
+ textAlign: 'center',
+ width: 'var(--input-height)',
+ },
+ },
+ variants: {
+ size: {
+ md: {
+ input: {
+ boxSize: 10,
+ borderRadius: 'base',
+ },
+ },
+ },
+ variant: mapEntries(variants!.variant, (key, value) => [
+ key,
+ { input: value },
+ ]),
+ },
+ defaultVariants: {
+ size: 'md',
+ variant: 'outline',
+ },
+});
diff --git a/toolkit/theme/recipes/popover.recipe.ts b/toolkit/theme/recipes/popover.recipe.ts
new file mode 100644
index 0000000000..5dbd279f05
--- /dev/null
+++ b/toolkit/theme/recipes/popover.recipe.ts
@@ -0,0 +1,71 @@
+import { defineSlotRecipe } from '@chakra-ui/react';
+
+export const recipe = defineSlotRecipe({
+ slots: [ 'content', 'header', 'body', 'footer', 'arrow', 'arrowTip' ],
+ base: {
+ content: {
+ position: 'relative',
+ display: 'flex',
+ flexDirection: 'column',
+ textStyle: 'sm',
+ '--popover-bg': 'colors.popover.bg',
+ bg: 'var(--popover-bg)',
+ boxShadow: 'popover',
+ boxShadowColor: 'colors.popover.shadow',
+ '--popover-mobile-size': 'calc(100dvw - 1rem)',
+ width: 'fit-content',
+ maxW: {
+ base: 'calc(100vw - 8px)',
+ lg: '800px',
+ },
+ borderWidth: '0',
+ borderRadius: 'md',
+ '--popover-z-index': 'zIndex.popover',
+ zIndex: 'calc(var(--popover-z-index) + var(--layer-index, 0))',
+ outline: '0',
+ transformOrigin: 'var(--transform-origin)',
+ _open: {
+ animationStyle: 'scale-fade-in',
+ animationDuration: 'fast',
+ },
+ _closed: {
+ animationStyle: 'scale-fade-out',
+ animationDuration: 'faster',
+ },
+ },
+ header: {
+ paddingInline: 'var(--popover-padding)',
+ paddingTop: 'var(--popover-padding)',
+ },
+ body: {
+ padding: 'var(--popover-padding)',
+ flex: '1',
+ },
+ footer: {
+ display: 'flex',
+ alignItems: 'center',
+ paddingInline: 'var(--popover-padding)',
+ paddingBottom: 'var(--popover-padding)',
+ },
+ arrow: {
+ '--arrow-size': 'sizes.3',
+ '--arrow-background': 'var(--popover-bg)',
+ },
+ arrowTip: {
+ borderTopWidth: '1px',
+ borderInlineStartWidth: '1px',
+ },
+ },
+ variants: {
+ size: {
+ sm: {
+ content: {
+ '--popover-padding': 'spacing.4',
+ },
+ },
+ },
+ },
+ defaultVariants: {
+ size: 'sm',
+ },
+});
diff --git a/toolkit/theme/recipes/progress-circle.recipe.ts b/toolkit/theme/recipes/progress-circle.recipe.ts
new file mode 100644
index 0000000000..a71a3ab33e
--- /dev/null
+++ b/toolkit/theme/recipes/progress-circle.recipe.ts
@@ -0,0 +1,74 @@
+import { defineSlotRecipe } from '@chakra-ui/react';
+
+export const recipe = defineSlotRecipe({
+ slots: [ 'root', 'circle', 'circleTrack', 'circleRange', 'label', 'valueText' ],
+ base: {
+ root: {
+ display: 'inline-flex',
+ textStyle: 'sm',
+ position: 'relative',
+ },
+ circle: {
+ _indeterminate: {
+ animation: 'spin 2s linear infinite',
+ },
+ },
+ circleTrack: {
+ '--track-color': 'colors.progressCircle.trackColor',
+ stroke: 'var(--track-color)',
+ },
+ circleRange: {
+ stroke: 'blue.500',
+ transitionProperty: 'stroke-dasharray',
+ transitionDuration: '0.6s',
+ _indeterminate: {
+ animation: 'circular-progress 1.5s linear infinite',
+ },
+ },
+ label: {
+ display: 'inline-flex',
+ },
+ valueText: {
+ lineHeight: '1',
+ fontWeight: 'medium',
+ letterSpacing: 'tight',
+ fontVariantNumeric: 'tabular-nums',
+ },
+ },
+
+ variants: {
+ size: {
+ sm: {
+ circle: {
+ '--size': '20px',
+ '--thickness': '2px',
+ },
+ valueText: {
+ textStyle: 'xs',
+ },
+ },
+ md: {
+ circle: {
+ '--size': '32px',
+ '--thickness': '3px',
+ },
+ valueText: {
+ textStyle: 'xs',
+ },
+ },
+ lg: {
+ circle: {
+ '--size': '80px',
+ '--thickness': '8px',
+ },
+ valueText: {
+ textStyle: 'sm',
+ },
+ },
+ },
+ },
+
+ defaultVariants: {
+ size: 'md',
+ },
+});
diff --git a/toolkit/theme/recipes/radio-group.recipe.ts b/toolkit/theme/recipes/radio-group.recipe.ts
new file mode 100644
index 0000000000..4e925984a0
--- /dev/null
+++ b/toolkit/theme/recipes/radio-group.recipe.ts
@@ -0,0 +1,73 @@
+import { defineSlotRecipe } from '@chakra-ui/react';
+
+import { recipe as radiomarkRecipe } from './radiomark.recipe';
+
+export const recipe = defineSlotRecipe({
+ slots: [ 'item', 'itemControl', 'itemText', 'root' ],
+ base: {
+ root: {
+ display: 'flex',
+ },
+
+ item: {
+ display: 'inline-flex',
+ alignItems: 'center',
+ position: 'relative',
+ fontWeight: 'normal',
+ cursor: 'pointer',
+ _disabled: {
+ cursor: 'disabled',
+ },
+ _readOnly: {
+ cursor: 'default',
+ },
+ },
+
+ itemControl: radiomarkRecipe.base,
+
+ itemText: {
+ userSelect: 'none',
+ textStyle: 'md',
+ _disabled: {
+ opacity: 'control.disabled',
+ },
+ },
+ },
+ variants: {
+ variant: {
+ solid: {
+ itemControl: radiomarkRecipe.variants?.variant?.solid,
+ },
+ },
+
+ size: {
+ md: {
+ item: { textStyle: 'md', gap: '2' },
+ itemControl: radiomarkRecipe.variants?.size?.md,
+ },
+ },
+
+ orientation: {
+ vertical: {
+ root: {
+ flexDirection: 'column',
+ alignItems: 'flex-start',
+ rowGap: '12px',
+ },
+ },
+ horizontal: {
+ root: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ columnGap: '32px',
+ },
+ },
+ },
+ },
+
+ defaultVariants: {
+ size: 'md',
+ variant: 'solid',
+ orientation: 'vertical',
+ },
+});
diff --git a/toolkit/theme/recipes/radiomark.recipe.ts b/toolkit/theme/recipes/radiomark.recipe.ts
new file mode 100644
index 0000000000..5812ff903e
--- /dev/null
+++ b/toolkit/theme/recipes/radiomark.recipe.ts
@@ -0,0 +1,91 @@
+import { defineRecipe } from '@chakra-ui/react';
+
+export const recipe = defineRecipe({
+ base: {
+ display: 'inline-flex',
+ alignItems: 'center',
+ justifyContent: 'center',
+ flexShrink: 0,
+ verticalAlign: 'top',
+ color: 'white',
+ borderWidth: '2px',
+ borderColor: 'transparent',
+ borderRadius: 'full',
+ _focusVisible: {
+ outline: '2px solid',
+ outlineColor: 'colorPalette.focusRing',
+ outlineOffset: '2px',
+ },
+ _disabled: {
+ cursor: 'disabled',
+ opacity: 'control.disabled',
+ },
+
+ '& .dot': {
+ height: '100%',
+ width: '100%',
+ borderRadius: 'full',
+ bg: 'currentColor',
+ scale: '0.4',
+ },
+ },
+
+ variants: {
+ variant: {
+ solid: {
+ borderWidth: '2px',
+ borderColor: 'radio.control.border',
+ _hover: {
+ borderColor: 'radio.control.border.hover',
+ },
+ _checked: {
+ bg: 'blue.500',
+ color: 'white',
+ borderColor: 'blue.500',
+ _hover: {
+ bg: 'blue.400',
+ borderColor: 'blue.400',
+ },
+ },
+ _invalid: {
+ bg: 'red.500',
+ borderColor: 'red.500',
+ },
+ _readOnly: {
+ borderColor: 'radio.control.border.readOnly',
+ _hover: {
+ borderColor: 'radio.control.border.readOnly',
+ },
+ _checked: {
+ bg: 'radio.control.border.readOnly',
+ _hover: {
+ bg: 'radio.control.border.readOnly',
+ },
+ '& .dot': {
+ bg: 'gray.500',
+ },
+ },
+ },
+ },
+ },
+
+ size: {
+ xs: {
+ boxSize: '3',
+ },
+
+ sm: {
+ boxSize: '4',
+ },
+
+ md: {
+ boxSize: '5',
+ },
+ },
+ },
+
+ defaultVariants: {
+ variant: 'solid',
+ size: 'md',
+ },
+});
diff --git a/toolkit/theme/recipes/rating-group.recipe.ts b/toolkit/theme/recipes/rating-group.recipe.ts
new file mode 100644
index 0000000000..1671b945ed
--- /dev/null
+++ b/toolkit/theme/recipes/rating-group.recipe.ts
@@ -0,0 +1,93 @@
+import { defineSlotRecipe } from '@chakra-ui/react';
+
+export const recipe = defineSlotRecipe({
+ className: 'chakra-rating-group',
+ slots: [ 'root', 'control', 'item', 'itemIndicator' ],
+ base: {
+ root: {
+ display: 'inline-flex',
+ alignItems: 'center',
+ columnGap: 3,
+ },
+
+ control: {
+ display: 'inline-flex',
+ alignItems: 'center',
+ gap: 1,
+ },
+
+ item: {
+ display: 'inline-flex',
+ alignItems: 'center',
+ justifyContent: 'center',
+ userSelect: 'none',
+ cursor: 'pointer',
+
+ _icon: {
+ width: '100%',
+ height: '100%',
+ display: 'inline-block',
+ flexShrink: 0,
+ position: 'absolute',
+ left: 0,
+ top: 0,
+ },
+ },
+
+ itemIndicator: {
+ display: 'inline-flex',
+ alignItems: 'center',
+ justifyContent: 'center',
+ position: 'relative',
+
+ _icon: {
+ stroke: 'none',
+ width: '100%',
+ height: '100%',
+ display: 'inline-block',
+ flexShrink: 0,
+ position: 'absolute',
+ left: 0,
+ top: 0,
+ },
+
+ '& [data-bg]': {
+ color: 'rating',
+ },
+
+ '& [data-fg]': {
+ color: 'transparent',
+ },
+
+ '&[data-highlighted]:not([data-half])': {
+ '& [data-fg]': {
+ color: 'rating.highlighted',
+ },
+ },
+
+ '&[data-half]': {
+ '& [data-fg]': {
+ color: 'rating.highlighted',
+ clipPath: 'inset(0 50% 0 0)',
+ },
+ },
+ },
+ },
+
+ variants: {
+ size: {
+ md: {
+ item: {
+ boxSize: 5,
+ },
+ root: {
+ textStyle: 'md',
+ },
+ },
+ },
+ },
+
+ defaultVariants: {
+ size: 'md',
+ },
+});
diff --git a/toolkit/theme/recipes/select.recipe.ts b/toolkit/theme/recipes/select.recipe.ts
new file mode 100644
index 0000000000..e272ab49d0
--- /dev/null
+++ b/toolkit/theme/recipes/select.recipe.ts
@@ -0,0 +1,277 @@
+import { defineSlotRecipe } from '@chakra-ui/react';
+
+export const recipe = defineSlotRecipe({
+ slots: [ 'root', 'trigger', 'indicatorGroup', 'indicator', 'content', 'item', 'control', 'itemText', 'itemGroup', 'itemGroupLabel', 'label', 'valueText' ],
+ base: {
+ root: {
+ display: 'flex',
+ flexDirection: 'column',
+ gap: '1.5',
+ width: '100%',
+ },
+ trigger: {
+ display: 'flex',
+ alignItems: 'center',
+ justifyContent: 'flex-start',
+ width: 'full',
+ minH: 'var(--select-trigger-height)',
+ pr: 'var(--select-trigger-padding-right)',
+ pl: 'var(--select-trigger-padding-left)',
+ borderRadius: 'base',
+ userSelect: 'none',
+ textAlign: 'start',
+ fontWeight: 'semibold',
+ cursor: 'pointer',
+ focusVisibleRing: 'none',
+ _disabled: {
+ opacity: 'control.disabled',
+ },
+ _placeholderShown: {
+ '& [data-part=value-text]': {
+ display: '-webkit-box',
+ },
+ },
+ },
+ indicatorGroup: {
+ display: 'flex',
+ alignItems: 'center',
+ gap: '1',
+ pos: 'absolute',
+ right: '0',
+ top: '0',
+ bottom: '0',
+ px: '0',
+ pointerEvents: 'none',
+ },
+ indicator: {
+ display: 'flex',
+ alignItems: 'center',
+ justifyContent: 'center',
+ boxSize: '5',
+ color: 'inherit',
+ _open: {
+ color: 'link.primary.hover',
+ },
+ },
+ content: {
+ background: 'popover.bg',
+ display: 'flex',
+ flexDirection: 'column',
+ zIndex: 'popover',
+ borderRadius: 'md',
+ borderWidth: '0',
+ outline: 0,
+ boxShadow: 'popover',
+ boxShadowColor: 'colors.popover.shadow',
+ maxH: '96',
+ overflowY: 'auto',
+ minWidth: '150px',
+ rowGap: '2',
+ _open: {
+ animationStyle: 'slide-fade-in',
+ animationDuration: 'fast',
+ },
+ _closed: {
+ animationStyle: 'slide-fade-out',
+ animationDuration: 'fastest',
+ },
+ },
+ item: {
+ position: 'relative',
+ userSelect: 'none',
+ display: 'flex',
+ alignItems: 'center',
+ gap: '2',
+ cursor: 'pointer',
+ justifyContent: 'flex-start',
+ flex: '1',
+ textAlign: 'start',
+ borderRadius: 'none',
+ _disabled: {
+ pointerEvents: 'none',
+ opacity: 'control.disabled',
+ },
+ _highlighted: {
+ bg: 'select.item.bg.highlighted',
+ },
+ },
+ control: {
+ pos: 'relative',
+ },
+ itemText: {
+ flex: '1',
+ },
+ itemGroup: {
+ _first: { mt: '0' },
+ },
+ itemGroupLabel: {
+ py: '1',
+ fontWeight: 'medium',
+ },
+ label: {
+ fontWeight: 'medium',
+ userSelect: 'none',
+ textStyle: 'sm',
+ _disabled: {
+ opacity: 'control.disabled',
+ },
+ },
+ valueText: {
+ display: 'flex',
+ flexDirection: 'column',
+ lineClamp: '1',
+ maxW: '100%',
+ wordBreak: 'break-all',
+ },
+ },
+
+ variants: {
+ variant: {
+ outline: {
+ trigger: {
+ borderWidth: '2px',
+ bg: 'input.bg',
+ color: 'select.trigger.outline.fg',
+ borderColor: 'input.border.filled',
+ _expanded: {
+ color: 'link.primary.hover',
+ borderColor: 'link.primary.hover',
+ _hover: {
+ color: 'link.primary.hover',
+ borderColor: 'link.primary.hover',
+ },
+ },
+ _hover: {
+ color: 'select.trigger.outline.fg',
+ borderColor: 'input.border.hover',
+ },
+ _focusVisible: {
+ borderColor: 'input.border.focus',
+ },
+ _readOnly: {
+ userSelect: 'all',
+ pointerEvents: 'none',
+ cursor: 'default',
+ bg: 'input.bg.readOnly',
+ borderColor: 'input.border.readOnly',
+ _focus: {
+ borderColor: 'input.border.readOnly',
+ },
+ _hover: {
+ borderColor: 'input.border.readOnly',
+ },
+ },
+ _invalid: {
+ borderColor: 'input.border.error',
+ _hover: {
+ borderColor: 'input.border.error',
+ },
+ _expanded: {
+ color: 'link.primary.hover',
+ borderColor: 'link.primary.hover',
+ _hover: {
+ color: 'link.primary.hover',
+ borderColor: 'link.primary.hover',
+ },
+ },
+ },
+ _placeholderShown: {
+ color: 'select.placeholder.fg',
+ borderColor: 'input.border',
+ _hover: {
+ color: 'select.placeholder.fg',
+ },
+ _invalid: {
+ color: 'select.placeholder.fg.error',
+ _hover: {
+ color: 'select.placeholder.fg.error',
+ },
+ },
+ },
+ },
+ indicatorGroup: {
+ color: 'select.indicator.fg',
+ _peerDisabled: {
+ opacity: 'control.disabled',
+ },
+ },
+ },
+ plain: {
+ trigger: {},
+ indicatorGroup: {},
+ },
+ },
+
+ size: {
+ sm: {
+ root: {
+ '--select-trigger-height': 'sizes.8',
+ '--select-trigger-padding-right': 'spacing.8',
+ '--select-trigger-padding-left': 'spacing.2',
+ },
+ content: {
+ px: '0',
+ py: '4',
+ textStyle: 'md',
+ },
+ trigger: {
+ textStyle: 'sm',
+ gap: '1',
+ },
+ indicatorGroup: {
+ pr: '2',
+ pl: '1',
+ },
+ item: {
+ py: '5px',
+ px: '4',
+ },
+ },
+ lg: {
+ root: {
+ '--select-trigger-height': '60px',
+ '--select-trigger-padding-right': '44px',
+ '--select-trigger-padding-left': 'spacing.4',
+ },
+ content: {
+ px: '0',
+ py: '4',
+ textStyle: 'md',
+ },
+ trigger: {
+ py: '2',
+ },
+ item: {
+ py: '5px',
+ px: '4',
+ },
+ indicatorGroup: {
+ pr: '4',
+ pl: '2',
+ },
+ },
+ },
+ },
+
+ compoundVariants: [
+ {
+ size: 'sm',
+ variant: 'outline',
+ css: {
+ trigger: {
+ _placeholderShown: {
+ color: 'select.trigger.outline.fg',
+ _hover: {
+ color: 'select.trigger.outline.fg',
+ },
+ },
+ },
+ },
+ },
+ ],
+
+ defaultVariants: {
+ size: 'sm',
+ variant: 'outline',
+ },
+});
diff --git a/toolkit/theme/recipes/skeleton.recipe.ts b/toolkit/theme/recipes/skeleton.recipe.ts
new file mode 100644
index 0000000000..919578ac8f
--- /dev/null
+++ b/toolkit/theme/recipes/skeleton.recipe.ts
@@ -0,0 +1,56 @@
+import { defineRecipe } from '@chakra-ui/react';
+
+export const recipe = defineRecipe({
+ base: {},
+
+ variants: {
+ loading: {
+ // special value to override the default behavior of the skeleton in Chakra
+ // it uses "background: unset" when the loading prop is set to false
+ // but it causes issues with background color of child element (e.g. button, badge, etc.)
+ // so, instead of the "loading" prop, we use the "state" prop to control the skeleton (see below)
+ reset: {},
+ },
+ state: {
+ loading: {
+ borderRadius: 'base',
+ boxShadow: 'none',
+ backgroundClip: 'padding-box',
+ cursor: 'default',
+ color: 'transparent',
+ borderWidth: '0px',
+ pointerEvents: 'none',
+ userSelect: 'none',
+ '&::before, &::after, *': {
+ visibility: 'hidden',
+ },
+ },
+ },
+ variant: {
+ pulse: {
+ background: 'bg.emphasized',
+ animation: 'pulse',
+ animationDuration: 'var(--duration, 1.2s)',
+ },
+ shine: {
+ '--animate-from': '100%',
+ '--animate-to': '-100%',
+ '--start-color': 'colors.skeleton.bg.start',
+ '--end-color': 'colors.skeleton.bg.end',
+ backgroundImage:
+ 'linear-gradient(90deg,var(--start-color) 8%,var(--end-color) 18%,var(--start-color) 33%)',
+ backgroundColor: 'transparent',
+ backgroundSize: '200% 100%',
+ animation: 'bg-position var(--duration, 2s) linear infinite',
+ },
+ none: {
+ animation: 'none',
+ },
+ },
+ },
+
+ defaultVariants: {
+ variant: 'shine',
+ loading: 'reset',
+ },
+});
diff --git a/toolkit/theme/recipes/spinner.recipe.ts b/toolkit/theme/recipes/spinner.recipe.ts
new file mode 100644
index 0000000000..cae5cb9835
--- /dev/null
+++ b/toolkit/theme/recipes/spinner.recipe.ts
@@ -0,0 +1,31 @@
+import { defineRecipe } from '@chakra-ui/react';
+
+export const recipe = defineRecipe({
+ base: {
+ display: 'inline-block',
+ borderColor: 'blue.500',
+ borderStyle: 'solid',
+ borderWidth: '2px',
+ borderRadius: 'full',
+ width: 'var(--spinner-size)',
+ height: 'var(--spinner-size)',
+ animation: 'spin',
+ animationDuration: 'slowest',
+ '--spinner-track-color': '{colors.spinner.track}',
+ borderBottomColor: 'var(--spinner-track-color)',
+ borderInlineStartColor: 'var(--spinner-track-color)',
+ },
+ variants: {
+ size: {
+ inherit: { '--spinner-size': '1em' },
+ xs: { '--spinner-size': 'sizes.3' },
+ sm: { '--spinner-size': 'sizes.4' },
+ md: { '--spinner-size': 'sizes.5' },
+ lg: { '--spinner-size': 'sizes.8' },
+ xl: { '--spinner-size': 'sizes.10' },
+ },
+ },
+ defaultVariants: {
+ size: 'md',
+ },
+});
diff --git a/toolkit/theme/recipes/stat.recipe.ts b/toolkit/theme/recipes/stat.recipe.ts
new file mode 100644
index 0000000000..3431ad23e7
--- /dev/null
+++ b/toolkit/theme/recipes/stat.recipe.ts
@@ -0,0 +1,101 @@
+import { defineSlotRecipe } from '@chakra-ui/react';
+
+export const recipe = defineSlotRecipe({
+ slots: [ 'root', 'label', 'helpText', 'valueUnit', 'valueText', 'indicator' ],
+ base: {
+ root: {
+ display: 'flex',
+ flexDirection: 'column',
+ gap: '1',
+ position: 'relative',
+ flex: '1',
+ },
+ label: {
+ display: 'inline-flex',
+ gap: '1.5',
+ alignItems: 'center',
+ color: 'text',
+ textStyle: 'sm',
+ },
+ helpText: {
+ color: 'text',
+ textStyle: 'xs',
+ },
+ valueUnit: {
+ color: 'text',
+ textStyle: 'xs',
+ fontWeight: 'initial',
+ letterSpacing: 'initial',
+ },
+ valueText: {
+ verticalAlign: 'baseline',
+ fontWeight: 'semibold',
+ letterSpacing: 'normal',
+ fontFeatureSettings: 'initial',
+ fontVariantNumeric: 'initial',
+ display: 'inline-flex',
+ gap: '1',
+ },
+ indicator: {
+ display: 'inline-flex',
+ alignItems: 'center',
+ justifyContent: 'center',
+ marginEnd: 0,
+ '& :where(svg)': {
+ w: '1em',
+ h: '1em',
+ },
+ '&[data-type=up]': {
+ color: 'stat.indicator.up',
+ },
+ '&[data-type=down]': {
+ color: 'stat.indicator.down',
+ },
+ },
+ },
+
+ variants: {
+ orientation: {
+ horizontal: {
+ root: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ },
+ },
+ },
+ positive: {
+ 'true': {
+ valueText: {
+ color: 'stat.indicator.up',
+ },
+ },
+ 'false': {
+ valueText: {
+ color: 'stat.indicator.down',
+ },
+ },
+ },
+ size: {
+ sm: {
+ valueText: {
+ textStyle: 'sm',
+ },
+ },
+ md: {
+ valueText: {
+ textStyle: 'md',
+ },
+ },
+ lg: {
+ valueText: {
+ textStyle: 'lg',
+ },
+ },
+ },
+ },
+
+ defaultVariants: {
+ size: 'md',
+ orientation: 'horizontal',
+ },
+});
diff --git a/toolkit/theme/recipes/switch.recipe.ts b/toolkit/theme/recipes/switch.recipe.ts
new file mode 100644
index 0000000000..da551a7ab9
--- /dev/null
+++ b/toolkit/theme/recipes/switch.recipe.ts
@@ -0,0 +1,130 @@
+import { defineSlotRecipe } from '@chakra-ui/react';
+
+export const recipe = defineSlotRecipe({
+ slots: [ 'root', 'label', 'indicator', 'control', 'thumb' ],
+ className: 'chakra-switch',
+ base: {
+ root: {
+ display: 'inline-flex',
+ gap: '2.5',
+ alignItems: 'center',
+ position: 'relative',
+ verticalAlign: 'middle',
+ '--switch-diff': 'calc(var(--switch-width) - var(--switch-height))',
+ '--switch-x': {
+ base: 'var(--switch-diff)',
+ _rtl: 'calc(var(--switch-diff) * -1)',
+ },
+ },
+
+ label: {
+ lineHeight: '1',
+ userSelect: 'none',
+ fontSize: 'sm',
+ fontWeight: '400',
+ _disabled: {
+ opacity: '0.5',
+ },
+ },
+
+ indicator: {
+ position: 'absolute',
+ height: 'var(--switch-height)',
+ width: 'var(--switch-height)',
+ fontSize: 'var(--switch-indicator-font-size)',
+ flexShrink: 0,
+ userSelect: 'none',
+ display: 'grid',
+ placeContent: 'center',
+ transition: 'inset-inline-start 0.12s ease',
+ insetInlineStart: 'calc(var(--switch-x) - 2px)',
+ _checked: {
+ insetInlineStart: '2px',
+ },
+ },
+
+ control: {
+ display: 'inline-flex',
+ gap: '0.5rem',
+ flexShrink: 0,
+ justifyContent: 'flex-start',
+ cursor: 'switch',
+ borderRadius: 'full',
+ position: 'relative',
+ width: 'var(--switch-width)',
+ height: 'var(--switch-height)',
+ _disabled: {
+ opacity: '0.5',
+ cursor: 'not-allowed',
+ },
+ _invalid: {
+ outline: '2px solid',
+ outlineColor: 'border.error',
+ outlineOffset: '2px',
+ },
+ },
+
+ thumb: {
+ display: 'flex',
+ alignItems: 'center',
+ justifyContent: 'center',
+ flexShrink: 0,
+ transitionProperty: 'translate',
+ transitionDuration: 'fast',
+ borderRadius: 'inherit',
+ _checked: {
+ translate: 'var(--switch-x) 0',
+ },
+ },
+ },
+
+ variants: {
+ variant: {
+ primary: {
+ control: {
+ borderRadius: 'full',
+ bg: 'switch.primary.bg',
+ focusVisibleRing: 'outside',
+ _checked: {
+ bg: 'switch.primary.bg.checked',
+ _hover: {
+ bg: 'switch.primary.bg.hover',
+ },
+ },
+ },
+ thumb: {
+ bg: 'white',
+ width: 'var(--switch-height)',
+ height: 'var(--switch-height)',
+ scale: '0.8',
+ boxShadow: 'sm',
+ _checked: {
+ bg: 'white',
+ },
+ },
+ },
+ },
+
+ size: {
+ sm: {
+ root: {
+ '--switch-width': '26px',
+ '--switch-height': 'sizes.4',
+ '--switch-indicator-font-size': 'fontSizes.sm',
+ },
+ },
+ md: {
+ root: {
+ '--switch-width': '34px',
+ '--switch-height': 'sizes.5',
+ '--switch-indicator-font-size': 'fontSizes.md',
+ },
+ },
+ },
+ },
+
+ defaultVariants: {
+ variant: 'primary',
+ size: 'md',
+ },
+});
diff --git a/toolkit/theme/recipes/table.recipe.ts b/toolkit/theme/recipes/table.recipe.ts
new file mode 100644
index 0000000000..89511ce483
--- /dev/null
+++ b/toolkit/theme/recipes/table.recipe.ts
@@ -0,0 +1,84 @@
+import { defineSlotRecipe } from '@chakra-ui/react';
+
+export const recipe = defineSlotRecipe({
+ slots: [ 'root', 'row', 'cell', 'columnHeader', 'caption', 'footer', 'body', 'header' ],
+ base: {
+ root: {
+ tableLayout: 'fixed',
+ fontVariant: 'normal',
+ fontVariantLigatures: 'no-contextual',
+ borderCollapse: 'collapse',
+ width: 'full',
+ textAlign: 'start',
+ verticalAlign: 'top',
+ overflow: 'unset',
+ },
+ cell: {
+ textAlign: 'start',
+ alignItems: 'center',
+ verticalAlign: 'top',
+ fontWeight: 'medium',
+ },
+ columnHeader: {
+ fontWeight: 'medium',
+ textAlign: 'start',
+ },
+ },
+
+ variants: {
+ variant: {
+ line: {
+ columnHeader: {
+ color: 'table.header.fg',
+ backgroundColor: 'table.header.bg',
+ _first: {
+ borderTopLeftRadius: '8px',
+ },
+ _last: {
+ borderTopRightRadius: '8px',
+ },
+ },
+ cell: {
+ borderBottomWidth: '1px',
+ borderColor: 'border.divider',
+ },
+ row: {
+ bg: 'bg',
+ },
+ },
+ },
+
+ size: {
+ md: {
+ root: {
+ fontSize: 'sm',
+ },
+ columnHeader: {
+ px: '6px',
+ py: '10px',
+ _first: {
+ pl: 3,
+ },
+ _last: {
+ pr: 3,
+ },
+ },
+ cell: {
+ px: '6px',
+ py: 4,
+ _first: {
+ pl: 3,
+ },
+ _last: {
+ pr: 3,
+ },
+ },
+ },
+ },
+ },
+
+ defaultVariants: {
+ variant: 'line',
+ size: 'md',
+ },
+});
diff --git a/toolkit/theme/recipes/tabs.recipe.ts b/toolkit/theme/recipes/tabs.recipe.ts
new file mode 100644
index 0000000000..9c68fe1077
--- /dev/null
+++ b/toolkit/theme/recipes/tabs.recipe.ts
@@ -0,0 +1,219 @@
+import { defineSlotRecipe } from '@chakra-ui/react';
+
+export const recipe = defineSlotRecipe({
+ slots: [ 'root', 'list', 'trigger', 'content', 'indicator' ],
+ base: {
+ root: {
+ '--tabs-trigger-radius': 'radii.l2',
+ position: 'relative',
+ _horizontal: {
+ display: 'block',
+ },
+ _vertical: {
+ display: 'flex',
+ },
+ },
+ list: {
+ display: 'inline-flex',
+ width: '100%',
+ position: 'relative',
+ isolation: 'isolate',
+ '--tabs-indicator-shadow': 'shadows.xs',
+ '--tabs-indicator-bg': 'colors.bg',
+ minH: 'var(--tabs-height)',
+ _horizontal: {
+ flexDirection: 'row',
+ },
+ _vertical: {
+ flexDirection: 'column',
+ },
+ },
+ trigger: {
+ outline: '0',
+ minW: 'var(--tabs-height)',
+ height: 'var(--tabs-height)',
+ display: 'flex',
+ alignItems: 'center',
+ position: 'relative',
+ cursor: 'button',
+ gap: '2',
+ _focusVisible: {
+ zIndex: 1,
+ outline: '2px solid',
+ outlineColor: 'colorPalette.focusRing',
+ },
+ _disabled: {
+ cursor: 'not-allowed',
+ opacity: 0.5,
+ },
+ },
+ content: {
+ focusVisibleRing: 'inside',
+ _horizontal: {
+ width: '100%',
+ pt: 'var(--tabs-content-padding)',
+ },
+ _vertical: {
+ height: '100%',
+ ps: 'var(--tabs-content-padding)',
+ },
+ },
+ indicator: {
+ width: 'var(--width)',
+ height: 'var(--height)',
+ borderRadius: 'var(--tabs-indicator-radius)',
+ bg: 'var(--tabs-indicator-bg)',
+ shadow: 'var(--tabs-indicator-shadow)',
+ zIndex: -1,
+ },
+ },
+
+ variants: {
+ fitted: {
+ 'true': {
+ list: {
+ display: 'flex',
+ },
+ trigger: {
+ flex: 1,
+ textAlign: 'center',
+ justifyContent: 'center',
+ },
+ },
+ },
+
+ justify: {
+ start: {
+ list: {
+ justifyContent: 'flex-start',
+ },
+ },
+ center: {
+ list: {
+ justifyContent: 'center',
+ },
+ },
+ end: {
+ list: {
+ justifyContent: 'flex-end',
+ },
+ },
+ },
+
+ size: {
+ sm: {
+ root: {
+ '--tabs-height': 'sizes.8',
+ '--tabs-content-padding': 'spacing.6',
+ },
+ trigger: {
+ py: '1',
+ px: '3',
+ textStyle: 'sm',
+ },
+ },
+ md: {
+ root: {
+ '--tabs-height': 'sizes.10',
+ '--tabs-content-padding': 'spacing.6',
+ },
+ trigger: {
+ py: '2',
+ px: '4',
+ textStyle: 'md',
+ },
+ },
+ free: {},
+ },
+
+ variant: {
+ solid: {
+ trigger: {
+ fontWeight: '600',
+ gap: '1',
+ borderRadius: 'base',
+ color: 'tabs.solid.fg',
+ bg: 'transparent',
+ _selected: {
+ bg: 'tabs.solid.bg.selected',
+ color: 'tabs.solid.fg.selected',
+ _hover: {
+ color: 'tabs.solid.fg.selected',
+ },
+ },
+ _hover: {
+ color: 'link.primary.hover',
+ },
+ },
+ },
+ secondary: {
+ list: {
+ border: 'none',
+ columnGap: '2',
+ _horizontal: {
+ _before: {
+ display: 'none',
+ },
+ },
+ },
+ trigger: {
+ fontWeight: '500',
+ color: 'tabs.secondary.fg',
+ bg: 'transparent',
+ borderWidth: '2px',
+ borderStyle: 'solid',
+ borderColor: 'tabs.secondary.border',
+ borderRadius: 'base',
+ _selected: {
+ bg: 'tabs.secondary.bg.selected',
+ borderColor: 'transparent',
+ _hover: {
+ borderColor: 'transparent',
+ },
+ },
+ _hover: {
+ color: 'link.primary.hover',
+ borderColor: 'link.primary.hover',
+ },
+ },
+ },
+ segmented: {
+ trigger: {
+ color: 'tabs.segmented.fg',
+ bg: 'transparent',
+ borderWidth: '2px',
+ borderStyle: 'solid',
+ borderColor: 'tabs.segmented.border',
+ _hover: {
+ color: 'link.primary.hover',
+ },
+ _selected: {
+ color: 'tabs.segmented.fg.selected',
+ bg: 'tabs.segmented.border',
+ borderColor: 'tabs.segmented.border',
+ _hover: {
+ color: 'tabs.segmented.fg.selected',
+ },
+ },
+ _notFirst: {
+ borderLeftWidth: '0',
+ },
+ _first: {
+ borderTopLeftRadius: 'base',
+ borderBottomLeftRadius: 'base',
+ },
+ _last: {
+ borderTopRightRadius: 'base',
+ borderBottomRightRadius: 'base',
+ },
+ },
+ },
+ unstyled: {},
+ },
+ },
+
+ defaultVariants: {
+ size: 'md',
+ variant: 'solid',
+ },
+});
diff --git a/toolkit/theme/recipes/tag.recipe.ts b/toolkit/theme/recipes/tag.recipe.ts
new file mode 100644
index 0000000000..1136c6398c
--- /dev/null
+++ b/toolkit/theme/recipes/tag.recipe.ts
@@ -0,0 +1,145 @@
+import { defineSlotRecipe } from '@chakra-ui/react';
+
+export const recipe = defineSlotRecipe({
+ slots: [ 'root', 'label', 'startElement', 'endElement', 'closeTrigger' ],
+ base: {
+ root: {
+ display: 'inline-flex',
+ alignItems: 'center',
+ verticalAlign: 'top',
+ maxWidth: '100%',
+ userSelect: 'none',
+ borderRadius: 'sm',
+ focusVisibleRing: 'outside',
+ _loading: {
+ borderRadius: 'sm',
+ },
+ },
+ label: {
+ lineClamp: '1',
+ whiteSpace: 'nowrap',
+ textOverflow: 'ellipsis',
+ fontWeight: 'medium',
+ display: 'inline',
+ },
+ closeTrigger: {
+ display: 'flex',
+ alignItems: 'center',
+ justifyContent: 'center',
+ outline: '0',
+ borderRadius: 'none',
+ color: 'closeButton.fg',
+ focusVisibleRing: 'inside',
+ focusRingWidth: '2px',
+ _hover: {
+ color: 'link.primary.hover',
+ },
+ },
+ startElement: {
+ flexShrink: 0,
+ display: 'inline-flex',
+ alignItems: 'center',
+ justifyContent: 'center',
+ boxSize: 'var(--tag-element-size)',
+ ms: 'var(--tag-element-offset)',
+ '&:has([data-scope=avatar])': {
+ boxSize: 'var(--tag-avatar-size)',
+ ms: 'calc(var(--tag-element-offset) * 1.5)',
+ },
+ _icon: { boxSize: '100%' },
+ },
+ endElement: {
+ flexShrink: 0,
+ display: 'inline-flex',
+ alignItems: 'center',
+ justifyContent: 'center',
+ boxSize: 'var(--tag-element-size)',
+ me: 'var(--tag-element-offset)',
+ _icon: { boxSize: '100%' },
+ '&:has(button)': {
+ ms: 'calc(var(--tag-element-offset) * -1)',
+ },
+ },
+ },
+
+ variants: {
+ size: {
+ md: {
+ root: {
+ px: '1',
+ py: '0.5',
+ minH: '6',
+ gap: '1',
+ '--tag-avatar-size': 'spacing.4',
+ '--tag-element-size': 'spacing.3',
+ '--tag-element-offset': '0px',
+ },
+ label: {
+ textStyle: 'sm',
+ },
+ },
+ lg: {
+ root: {
+ px: '6px',
+ py: '6px',
+ minH: '8',
+ gap: '1',
+ '--tag-avatar-size': 'spacing.4',
+ '--tag-element-size': 'spacing.3',
+ '--tag-element-offset': '0px',
+ },
+ label: {
+ textStyle: 'sm',
+ },
+ },
+ },
+
+ variant: {
+ subtle: {
+ root: {
+ bgColor: 'tag.root.subtle.bg',
+ color: 'tag.root.subtle.fg',
+ },
+ },
+ clickable: {
+ root: {
+ cursor: 'pointer',
+ bgColor: 'tag.root.clickable.bg',
+ color: 'tag.root.clickable.fg',
+ _hover: {
+ opacity: 0.76,
+ },
+ },
+ },
+ filter: {
+ root: {
+ bgColor: 'tag.root.filter.bg',
+ },
+ },
+ select: {
+ root: {
+ cursor: 'pointer',
+ bgColor: 'tag.root.select.bg',
+ color: 'tag.root.select.fg',
+ _hover: {
+ color: 'blue.400',
+ opacity: 0.76,
+ },
+ _selected: {
+ bgColor: 'tag.root.select.bg.selected',
+ color: 'whiteAlpha.800',
+ _hover: {
+ color: 'whiteAlpha.800',
+ opacity: 0.76,
+ },
+ },
+ },
+ },
+ },
+ },
+
+ defaultVariants: {
+ size: 'md',
+ variant: 'subtle',
+ },
+});
diff --git a/toolkit/theme/recipes/textarea.recipe.ts b/toolkit/theme/recipes/textarea.recipe.ts
new file mode 100644
index 0000000000..3c569ac6e6
--- /dev/null
+++ b/toolkit/theme/recipes/textarea.recipe.ts
@@ -0,0 +1,86 @@
+import { defineRecipe } from '@chakra-ui/react';
+
+export const recipe = defineRecipe({
+ base: {
+ width: '100%',
+ minWidth: '0',
+ minHeight: '160px',
+ outline: '0',
+ position: 'relative',
+ appearance: 'none',
+ textAlign: 'start',
+ borderRadius: 'base',
+ color: 'input.fg',
+ '--focus-color': 'colors.border.error',
+ '--error-color': 'colors.border.error',
+ _invalid: {
+ focusRingColor: 'var(--error-color)',
+ borderColor: 'var(--error-color)',
+ },
+ },
+ variants: {
+ size: {
+ '2xl': {
+ textStyle: 'md',
+ py: '4',
+ pl: '4',
+ pr: '5', // === scrollbar width
+ scrollPaddingBottom: '4',
+ },
+ },
+
+ variant: {
+ outline: {
+ bg: 'input.bg',
+ borderWidth: '2px',
+ borderColor: 'input.border',
+ focusVisibleRing: 'none',
+ _hover: {
+ borderColor: 'input.border.hover',
+ },
+ _focus: {
+ borderColor: 'input.border.focus',
+ boxShadow: 'size.md',
+ _hover: {
+ borderColor: 'input.border.focus',
+ },
+ },
+ _readOnly: {
+ userSelect: 'all',
+ bg: 'input.bg.readOnly',
+ borderColor: 'input.border.readOnly',
+ _focus: {
+ borderColor: 'input.border.readOnly',
+ },
+ _hover: {
+ borderColor: 'input.border.readOnly',
+ },
+ },
+ _disabled: {
+ pointerEvents: 'none',
+ opacity: 'control.disabled',
+ },
+ _invalid: {
+ borderColor: 'input.border.error',
+ _hover: {
+ borderColor: 'input.border.error',
+ },
+ },
+ },
+ },
+
+ floating: {
+ 'true': {
+ paddingTop: '8',
+ _placeholderShown: {
+ paddingTop: '10',
+ },
+ },
+ },
+ },
+
+ defaultVariants: {
+ size: '2xl',
+ variant: 'outline',
+ },
+});
diff --git a/toolkit/theme/recipes/toast.recipe.ts b/toolkit/theme/recipes/toast.recipe.ts
new file mode 100644
index 0000000000..edbb674787
--- /dev/null
+++ b/toolkit/theme/recipes/toast.recipe.ts
@@ -0,0 +1,97 @@
+import { defineSlotRecipe } from '@chakra-ui/react';
+
+export const recipe = defineSlotRecipe({
+ slots: [ 'root', 'title', 'description', 'indicator', 'actionTrigger', 'closeTrigger' ],
+ base: {
+ root: {
+ width: 'full',
+ display: 'flex',
+ alignItems: 'flex-start',
+ position: 'relative',
+ gap: '3',
+ py: '3',
+ ps: '6',
+ pe: '3',
+ borderRadius: 'md',
+ translate: 'var(--x) var(--y)',
+ scale: 'var(--scale)',
+ zIndex: 'var(--z-index)',
+ height: 'var(--height)',
+ opacity: 'var(--opacity)',
+ willChange: 'translate, opacity, scale',
+ transition:
+ 'translate 400ms, scale 400ms, opacity 400ms, height 400ms, box-shadow 200ms',
+ transitionTimingFunction: 'cubic-bezier(0.21, 1.02, 0.73, 1)',
+ _closed: {
+ transition: 'translate 400ms, scale 400ms, opacity 200ms',
+ transitionTimingFunction: 'cubic-bezier(0.06, 0.71, 0.55, 1)',
+ },
+ bg: 'toast.bg.info',
+ color: 'toast.fg',
+ boxShadow: 'xl',
+ '--toast-trigger-bg': 'colors.bg.muted',
+ '&[data-type=warning]': {
+ color: 'toast.fg',
+ bg: 'toast.bg.warning',
+ '--toast-trigger-bg': '{white/10}',
+ '--toast-border-color': '{white/40}',
+ },
+ '&[data-type=success]': {
+ color: 'toast.fg',
+ bg: 'toast.bg.success',
+ '--toast-trigger-bg': '{white/10}',
+ '--toast-border-color': '{white/40}',
+ },
+ '&[data-type=error]': {
+ color: 'toast.fg',
+ bg: 'toast.bg.error',
+ '--toast-trigger-bg': '{white/10}',
+ '--toast-border-color': '{white/40}',
+ },
+ '&[data-type=info]': {
+ color: 'toast.fg',
+ bg: 'toast.bg.info',
+ '--toast-trigger-bg': '{white/10}',
+ '--toast-border-color': '{white/40}',
+ },
+ '&[data-type=loading]': {
+ color: 'toast.fg',
+ bg: 'toast.bg.info',
+ '--toast-trigger-bg': '{white/10}',
+ '--toast-border-color': '{white/40}',
+ },
+ },
+ title: {
+ fontWeight: '700',
+ textStyle: 'md',
+ marginEnd: '0',
+ },
+ description: {
+ display: 'inline',
+ textStyle: 'md',
+ },
+ indicator: {
+ flexShrink: '0',
+ boxSize: '5',
+ },
+ actionTrigger: {
+ textStyle: 'sm',
+ fontWeight: 'medium',
+ height: '8',
+ px: '3',
+ borderRadius: 'base',
+ alignSelf: 'center',
+ borderWidth: '1px',
+ borderColor: 'var(--toast-border-color, inherit)',
+ transition: 'background 200ms',
+ _hover: {
+ bg: 'var(--toast-trigger-bg)',
+ },
+ },
+ closeTrigger: {
+ position: 'static',
+ alignSelf: 'center',
+ color: 'closeButton.fg',
+ },
+ },
+});
diff --git a/toolkit/theme/recipes/tooltip.recipe.ts b/toolkit/theme/recipes/tooltip.recipe.ts
new file mode 100644
index 0000000000..4acea64d12
--- /dev/null
+++ b/toolkit/theme/recipes/tooltip.recipe.ts
@@ -0,0 +1,85 @@
+import { defineSlotRecipe } from '@chakra-ui/react';
+
+export const recipe = defineSlotRecipe({
+ slots: [ 'content', 'arrow', 'arrowTip' ],
+ base: {
+ content: {
+ px: '2',
+ py: '1',
+ borderRadius: 'sm',
+ fontWeight: '500',
+ textStyle: 'sm',
+ textAlign: 'center',
+ boxShadow: 'size.md',
+ zIndex: 'tooltip',
+ maxW: '320px',
+ transformOrigin: 'var(--transform-origin)',
+ _open: {
+ animationStyle: 'scale-fade-in',
+ animationDuration: 'fast',
+ },
+ _closed: {
+ animationStyle: 'scale-fade-out',
+ animationDuration: 'fast',
+ },
+ },
+ arrow: {
+ '--arrow-size': 'sizes.2',
+ '--arrow-background': 'var(--tooltip-bg)',
+ },
+ arrowTip: {
+ borderTopWidth: '1px',
+ borderInlineStartWidth: '1px',
+ borderColor: 'var(--tooltip-bg)',
+ },
+ },
+ variants: {
+ variant: {
+ regular: {
+ content: {
+ '--tooltip-bg': 'colors.tooltip.bg',
+ bg: 'var(--tooltip-bg)',
+ color: 'tooltip.fg',
+ },
+ },
+ navigation: {
+ content: {
+ '--tooltip-bg': 'colors.tooltip.navigation.bg',
+ bg: 'var(--tooltip-bg)',
+ color: 'tooltip.navigation.fg',
+ borderWidth: '0',
+ borderRadius: 'base',
+ minW: '120px',
+ boxShadow: 'none',
+ textAlign: 'center',
+ padding: '15px 12px',
+ _selected: {
+ color: 'tooltip.navigation.fg.selected',
+ },
+ },
+ arrow: {
+ display: 'none',
+ },
+ arrowTip: {
+ display: 'none',
+ },
+ },
+ popover: {
+ content: {
+ maxW: 'none',
+ bg: 'popover.bg',
+ color: 'text.primary',
+ p: '4',
+ boxShadow: 'popover',
+ boxShadowColor: 'popover.shadow',
+ borderRadius: 'md',
+ textAlign: 'left',
+ fontWeight: 'normal',
+ },
+ },
+ },
+ },
+ defaultVariants: {
+ variant: 'regular',
+ },
+});
diff --git a/toolkit/theme/theme.ts b/toolkit/theme/theme.ts
new file mode 100644
index 0000000000..f6f1f15cb2
--- /dev/null
+++ b/toolkit/theme/theme.ts
@@ -0,0 +1,41 @@
+import { createSystem, defaultConfig, defineConfig } from '@chakra-ui/react';
+
+import { keyframes } from './foundations/animations';
+import * as borders from './foundations/borders';
+import breakpoints from './foundations/breakpoints';
+import colors from './foundations/colors';
+import durations from './foundations/durations';
+import semanticTokens from './foundations/semanticTokens';
+import shadows from './foundations/shadows';
+import { fonts, textStyles } from './foundations/typography';
+import zIndex from './foundations/zIndex';
+import globalCss from './globalCss';
+import { recipes, slotRecipes } from './recipes';
+
+const customConfig = defineConfig({
+ globalCss,
+ theme: {
+ breakpoints,
+ keyframes,
+ recipes,
+ slotRecipes,
+ semanticTokens,
+ textStyles,
+ tokens: {
+ ...borders,
+ colors,
+ durations,
+ fonts,
+ shadows,
+ zIndex,
+ fontWeights: {
+ normal: { value: '400' },
+ medium: { value: '500' },
+ semibold: { value: '600' },
+ bold: { value: '700' },
+ },
+ },
+ },
+});
+
+export default createSystem(defaultConfig, customConfig);
diff --git a/toolkit/theme/utils/entries.ts b/toolkit/theme/utils/entries.ts
new file mode 100644
index 0000000000..1205e3dbde
--- /dev/null
+++ b/toolkit/theme/utils/entries.ts
@@ -0,0 +1,12 @@
+// https://github.com/chakra-ui/chakra-ui/blob/main/packages/react/src/utils/entries.ts#L1
+export function mapEntries(
+ obj: { [key in K]: A },
+ f: (key: K, val: A) => [K, B],
+): { [key in K]: B } {
+ const result: { [key in K]: B } = {} as unknown as { [key in K]: B };
+ for (const key in obj) {
+ const kv = f(key, obj[key]);
+ result[kv[0]] = kv[1];
+ }
+ return result;
+}
diff --git a/toolkit/utils/getComponentDisplayName.ts b/toolkit/utils/getComponentDisplayName.ts
new file mode 100644
index 0000000000..12893671a7
--- /dev/null
+++ b/toolkit/utils/getComponentDisplayName.ts
@@ -0,0 +1,11 @@
+import type React from 'react';
+
+export default function getComponentDisplayName(type: string | React.JSXElementConstructor) {
+ if (typeof type === 'string') {
+ return;
+ }
+
+ if ('displayName' in type) {
+ return type.displayName as string;
+ }
+}
diff --git a/tools/preset-sync/index.ts b/tools/preset-sync/index.ts
index 6feccfe352..e1982823e3 100755
--- a/tools/preset-sync/index.ts
+++ b/tools/preset-sync/index.ts
@@ -11,13 +11,15 @@ const PRESETS = {
eth: 'https://eth.blockscout.com',
eth_goerli: 'https://eth-goerli.blockscout.com',
eth_sepolia: 'https://eth-sepolia.blockscout.com',
- garnet: 'https://explorer.garnetchain.com',
filecoin: 'https://filecoin.blockscout.com',
+ garnet: 'https://explorer.garnetchain.com',
gnosis: 'https://gnosis.blockscout.com',
+ immutable: 'https://explorer.immutable.com',
mekong: 'https://mekong.blockscout.com',
neon_devnet: 'https://neon-devnet.blockscout.com',
optimism: 'https://optimism.blockscout.com',
optimism_celestia: 'https://opcelestia-raspberry.gelatoscout.com',
+ optimism_interop_0: 'https://optimism-interop-alpha-0.blockscout.com',
optimism_sepolia: 'https://optimism-sepolia.blockscout.com',
polygon: 'https://polygon.blockscout.com',
rari_testnet: 'https://rari-testnet.cloud.blockscout.com',
diff --git a/tsconfig.json b/tsconfig.json
index 29c8095af6..b93f43ec24 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -8,13 +8,14 @@
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"esModuleInterop": true,
- "module": "esnext",
- "moduleResolution": "node",
+ "module": "ESNext",
+ "moduleResolution": "Bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true,
"baseUrl": ".",
+ "types": ["node", "jest"],
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.node.ts", "**/*.tsx", "**/*.pw.tsx", "decs.d.ts", "global.d.ts"],
"exclude": ["node_modules", "node_modules_linux", "./deploy/tools/envs-validator", "./deploy/tools/favicon-generator"],
diff --git a/types/api/addressMetadata.ts b/types/api/addressMetadata.ts
index 6840a8235f..71f419a29e 100644
--- a/types/api/addressMetadata.ts
+++ b/types/api/addressMetadata.ts
@@ -1,4 +1,4 @@
-import type { AlertStatus } from '@chakra-ui/react';
+import type { AlertProps } from 'toolkit/chakra/alert';
export interface AddressMetadataInfo {
addresses: Record
data?: string;
alertBgColor?: string;
alertTextColor?: string;
- alertStatus?: AlertStatus;
+ alertStatus?: AlertProps['status'];
} | null;
}
diff --git a/types/api/interop.ts b/types/api/interop.ts
new file mode 100644
index 0000000000..4b30637fcd
--- /dev/null
+++ b/types/api/interop.ts
@@ -0,0 +1,30 @@
+export interface ChainInfo {
+ chain_id: number;
+ chain_name: string | null;
+ chain_logo: string | null;
+ instance_url: string;
+}
+
+export type MessageStatus = 'Sent' | 'Relayed' | 'Failed';
+
+export interface InteropMessage {
+ init_transaction_hash: string;
+ init_chain?: ChainInfo | null;
+ nonce: number;
+ payload: string;
+ relay_chain?: ChainInfo | null;
+ relay_transaction_hash: string | null;
+ sender: string;
+ status: MessageStatus;
+ target: string;
+ timestamp: string;
+}
+
+export interface InteropMessageListResponse {
+ items: Array;
+ next_page_params?: {
+ init_transaction_hash: string;
+ items_count: number;
+ timestamp: number;
+ };
+}
diff --git a/types/api/rewards.ts b/types/api/rewards.ts
index 70df2d98b8..f09412d1d4 100644
--- a/types/api/rewards.ts
+++ b/types/api/rewards.ts
@@ -12,6 +12,8 @@ export type RewardsConfigResponse = {
export type RewardsCheckRefCodeResponse = {
valid: boolean;
+ is_custom: boolean;
+ reward: string | null;
};
export type RewardsNonceResponse = {
diff --git a/types/api/token.ts b/types/api/token.ts
index c223d70a03..3a850e89e3 100644
--- a/types/api/token.ts
+++ b/types/api/token.ts
@@ -59,10 +59,12 @@ export interface TokenInstance {
holder_address_hash: string | null;
image_url: string | null;
animation_url: string | null;
+ media_url?: string | null;
+ media_type?: string | null;
external_app_url: string | null;
metadata: Record | null;
owner: AddressParam | null;
- thumbnails: Partial> | null;
+ thumbnails: ({ original: string } & Partial, string>>) | null;
}
export interface TokenInstanceMetadataSocketMessage {
diff --git a/types/api/tokenTransfer.ts b/types/api/tokenTransfer.ts
index b34b78269a..e2d9a81621 100644
--- a/types/api/tokenTransfer.ts
+++ b/types/api/tokenTransfer.ts
@@ -1,5 +1,5 @@
import type { AddressParam } from './addressParams';
-import type { TokenInfo, TokenType } from './token';
+import type { TokenInfo, TokenInstance, TokenType } from './token';
export type Erc20TotalPayload = {
decimals: string | null;
@@ -8,20 +8,24 @@ export type Erc20TotalPayload = {
export type Erc721TotalPayload = {
token_id: string | null;
+ token_instance: TokenInstance | null;
};
export type Erc1155TotalPayload = {
decimals: string | null;
value: string;
token_id: string | null;
+ token_instance: TokenInstance | null;
};
export type Erc404TotalPayload = {
decimals: string;
value: string;
token_id: null;
+ token_instance: TokenInstance | null;
} | {
token_id: string;
+ token_instance: TokenInstance | null;
};
export type TokenTransfer = (
diff --git a/types/api/tokens.ts b/types/api/tokens.ts
index b7e2410fa2..4248036036 100644
--- a/types/api/tokens.ts
+++ b/types/api/tokens.ts
@@ -34,4 +34,4 @@ export interface TokensSorting {
export type TokensSortingField = TokensSorting['sort'];
-export type TokensSortingValue = `${ TokensSortingField }-${ TokensSorting['order'] }`;
+export type TokensSortingValue = `${ TokensSortingField }-${ TokensSorting['order'] }` | 'default';
diff --git a/types/api/transaction.ts b/types/api/transaction.ts
index e4e30ecf50..539cd62e56 100644
--- a/types/api/transaction.ts
+++ b/types/api/transaction.ts
@@ -3,6 +3,7 @@ import type { ArbitrumBatchStatus, ArbitrumL2TxData } from './arbitrumL2';
import type { BlockTransactionsResponse } from './block';
import type { DecodedInput } from './decodedInput';
import type { Fee } from './fee';
+import type { ChainInfo, MessageStatus } from './interop';
import type { NovesTxTranslation } from './noves';
import type { OptimisticL2WithdrawalStatus } from './optimisticL2';
import type { ScrollL2BlockStatus } from './scrollL2';
@@ -15,7 +16,7 @@ export type TransactionRevertReason = {
raw: string;
} | DecodedInput;
-type WrappedTransactionFields = 'decoded_input' | 'fee' | 'gas_limit' | 'gas_price' | 'hash' | 'max_fee_per_gas' |
+export type WrappedTransactionFields = 'decoded_input' | 'fee' | 'gas_limit' | 'gas_price' | 'hash' | 'max_fee_per_gas' |
'max_priority_fee_per_gas' | 'method' | 'nonce' | 'raw_input' | 'to' | 'type' | 'value';
export interface OpWithdrawal {
@@ -107,6 +108,8 @@ export type Transaction = {
scroll?: ScrollTransactionData;
// EIP-7702
authorization_list?: Array;
+ // Interop
+ op_interop?: InteropTransactionInfo;
};
type ArbitrumTransactionData = {
@@ -193,7 +196,7 @@ export interface TransactionsSorting {
export type TransactionsSortingField = TransactionsSorting['sort'];
-export type TransactionsSortingValue = `${ TransactionsSortingField }-${ TransactionsSorting['order'] }`;
+export type TransactionsSortingValue = `${ TransactionsSortingField }-${ TransactionsSorting['order'] }` | 'default';
export type ScrollTransactionData = {
l1_fee: string;
@@ -215,3 +218,15 @@ export interface TxAuthorization {
chain_id: number;
nonce: number;
}
+
+export interface InteropTransactionInfo {
+ nonce: number;
+ payload: string;
+ init_chain?: ChainInfo | null;
+ relay_chain?: ChainInfo | null;
+ init_transaction_hash?: string;
+ relay_transaction_hash?: string;
+ sender: string;
+ status: MessageStatus;
+ target: string;
+}
diff --git a/types/api/validators.ts b/types/api/validators.ts
index fcc211732f..620759f0c9 100644
--- a/types/api/validators.ts
+++ b/types/api/validators.ts
@@ -37,7 +37,7 @@ export interface ValidatorsStabilitySorting {
export type ValidatorsStabilitySortingField = ValidatorsStabilitySorting['sort'];
-export type ValidatorsStabilitySortingValue = `${ ValidatorsStabilitySortingField }-${ ValidatorsStabilitySorting['order'] }`;
+export type ValidatorsStabilitySortingValue = `${ ValidatorsStabilitySortingField }-${ ValidatorsStabilitySorting['order'] }` | 'default';
// Blackfort
@@ -68,7 +68,7 @@ export interface ValidatorsBlackfortSorting {
export type ValidatorsBlackfortSortingField = ValidatorsBlackfortSorting['sort'];
-export type ValidatorsBlackfortSortingValue = `${ ValidatorsBlackfortSortingField }-${ ValidatorsBlackfortSorting['order'] }`;
+export type ValidatorsBlackfortSortingValue = `${ ValidatorsBlackfortSortingField }-${ ValidatorsBlackfortSorting['order'] }` | 'default';
// Zilliqa
export interface ValidatorsZilliqaItem {
diff --git a/types/api/verifiedContracts.ts b/types/api/verifiedContracts.ts
index 975c46ed8b..da66d4c8c6 100644
--- a/types/api/verifiedContracts.ts
+++ b/types/api/verifiedContracts.ts
@@ -5,4 +5,4 @@ export interface VerifiedContractsSorting {
export type VerifiedContractsSortingField = VerifiedContractsSorting['sort'];
-export type VerifiedContractsSortingValue = `${ VerifiedContractsSortingField }-${ VerifiedContractsSorting['order'] }`;
+export type VerifiedContractsSortingValue = `${ VerifiedContractsSortingField }-${ VerifiedContractsSorting['order'] }` | 'default';
diff --git a/ui/address/AddressAccountHistory.tsx b/ui/address/AddressAccountHistory.tsx
index cd9d495b6a..5e83554a9f 100644
--- a/ui/address/AddressAccountHistory.tsx
+++ b/ui/address/AddressAccountHistory.tsx
@@ -1,5 +1,4 @@
-import { Box, Hide, Show, Table,
- Tbody, Th, Tr } from '@chakra-ui/react';
+import { Box } from '@chakra-ui/react';
import { useRouter } from 'next/router';
import React from 'react';
@@ -11,13 +10,13 @@ import useIsMounted from 'lib/hooks/useIsMounted';
import getQueryParamString from 'lib/router/getQueryParamString';
import { NOVES_TRANSLATE } from 'stubs/noves/NovesTranslate';
import { generateListStub } from 'stubs/utils';
+import { TableBody, TableColumnHeader, TableHeaderSticky, TableRoot, TableRow } from 'toolkit/chakra/table';
import AddressAccountHistoryTableItem from 'ui/address/accountHistory/AddressAccountHistoryTableItem';
import ActionBar from 'ui/shared/ActionBar';
import DataListDisplay from 'ui/shared/DataListDisplay';
import { getFromToValue } from 'ui/shared/Noves/utils';
import Pagination from 'ui/shared/pagination/Pagination';
import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages';
-import TheadSticky from 'ui/shared/TheadSticky';
import AddressAccountHistoryListItem from './accountHistory/AddressAccountHistoryListItem';
import AccountHistoryFilter from './AddressAccountHistoryFilter';
@@ -25,12 +24,11 @@ import AccountHistoryFilter from './AddressAccountHistoryFilter';
const getFilterValue = (getFilterValueFromQuery).bind(null, NovesHistoryFilterValues);
type Props = {
- scrollRef?: React.RefObject;
shouldRender?: boolean;
isQueryEnabled?: boolean;
};
-const AddressAccountHistory = ({ scrollRef, shouldRender = true, isQueryEnabled = true }: Props) => {
+const AddressAccountHistory = ({ shouldRender = true, isQueryEnabled = true }: Props) => {
const router = useRouter();
const isMounted = useIsMounted();
@@ -41,7 +39,6 @@ const AddressAccountHistory = ({ scrollRef, shouldRender = true, isQueryEnabled
const { data, isError, pagination, isPlaceholderData } = useQueryWithPages({
resourceName: 'noves_address_history',
pathParams: { address: currentAddress },
- scrollRef,
options: {
enabled: isQueryEnabled,
placeholderData: generateListStub<'noves_address_history'>(NOVES_TRANSLATE, 10, { hasNextPage: false, pageNumber: 1, pageSize: 10 }),
@@ -75,7 +72,7 @@ const AddressAccountHistory = ({ scrollRef, shouldRender = true, isQueryEnabled
const content = (
-
+
{ filteredData?.map((item, i) => (
)) }
-
+
-
-
-
-
-
+
+
+
+
+
Age
-
-
+
+
Action
-
-
+
+
From/To
-
-
-
-
+
+
+
+
{ filteredData?.map((item, i) => (
)) }
-
-
-
+
+
+
);
return (
+ >
+ { content }
+
);
};
diff --git a/ui/address/AddressAccountHistoryFilter.tsx b/ui/address/AddressAccountHistoryFilter.tsx
index b321051b27..7c202f0e99 100644
--- a/ui/address/AddressAccountHistoryFilter.tsx
+++ b/ui/address/AddressAccountHistoryFilter.tsx
@@ -1,3 +1,4 @@
+import { createListCollection } from '@chakra-ui/react';
import React from 'react';
import type { NovesHistoryFilterValue } from 'types/api/noves';
@@ -11,6 +12,8 @@ const OPTIONS = [
{ value: 'sent', label: 'Sent to' },
];
+const collection = createListCollection({ items: OPTIONS });
+
interface Props {
hasActiveFilter: boolean;
defaultFilter: NovesHistoryFilterValue;
@@ -24,11 +27,11 @@ const AccountHistoryFilter = ({ onFilterChange, defaultFilter, hasActiveFilter,
return (
);
};
diff --git a/ui/address/AddressBlocksValidated.tsx b/ui/address/AddressBlocksValidated.tsx
index 114d47c57b..f0b729fd1a 100644
--- a/ui/address/AddressBlocksValidated.tsx
+++ b/ui/address/AddressBlocksValidated.tsx
@@ -1,4 +1,4 @@
-import { Hide, Show, Table, Tbody, Th, Tr } from '@chakra-ui/react';
+import { Box } from '@chakra-ui/react';
import { useQueryClient } from '@tanstack/react-query';
import { useRouter } from 'next/router';
import React from 'react';
@@ -14,12 +14,12 @@ import useSocketMessage from 'lib/socket/useSocketMessage';
import { currencyUnits } from 'lib/units';
import { BLOCK } from 'stubs/block';
import { generateListStub } from 'stubs/utils';
+import { TableBody, TableColumnHeader, TableHeaderSticky, TableRoot, TableRow } from 'toolkit/chakra/table';
import ActionBar, { ACTION_BAR_HEIGHT_DESKTOP } from 'ui/shared/ActionBar';
import DataListDisplay from 'ui/shared/DataListDisplay';
import Pagination from 'ui/shared/pagination/Pagination';
import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages';
import * as SocketNewItemsNotice from 'ui/shared/SocketNewItemsNotice';
-import { default as Thead } from 'ui/shared/TheadSticky';
import AddressBlocksValidatedListItem from './blocksValidated/AddressBlocksValidatedListItem';
import AddressBlocksValidatedTableItem from './blocksValidated/AddressBlocksValidatedTableItem';
@@ -27,12 +27,11 @@ import AddressBlocksValidatedTableItem from './blocksValidated/AddressBlocksVali
const OVERLOAD_COUNT = 75;
interface Props {
- scrollRef?: React.RefObject;
shouldRender?: boolean;
isQueryEnabled?: boolean;
}
-const AddressBlocksValidated = ({ scrollRef, shouldRender = true, isQueryEnabled = true }: Props) => {
+const AddressBlocksValidated = ({ shouldRender = true, isQueryEnabled = true }: Props) => {
const [ socketAlert, setSocketAlert ] = React.useState('');
const [ newItemsCount, setNewItemsCount ] = React.useState(0);
@@ -44,7 +43,6 @@ const AddressBlocksValidated = ({ scrollRef, shouldRender = true, isQueryEnabled
const query = useQueryWithPages({
resourceName: 'address_blocks_validated',
pathParams: { hash: addressHash },
- scrollRef,
options: {
enabled: isQueryEnabled,
placeholderData: generateListStub<'address_blocks_validated'>(
@@ -104,19 +102,19 @@ const AddressBlocksValidated = ({ scrollRef, shouldRender = true, isQueryEnabled
const content = query.data?.items ? (
<>
-
-
-
-
- Block
- Age
- Txn
- Gas used
+
+
+
+
+ Block
+ Age
+ Txn
+ Gas used
{ !config.UI.views.block.hiddenFields?.total_reward && !config.features.rollup.isEnabled &&
- Reward { currencyUnits.ether } }
-
-
-
+ Reward { currencyUnits.ether } }
+
+
+
)) }
-
-
-
-
+
+
+
+
{ query.pagination.page === 1 && (
)) }
-
+
>
) : null;
@@ -166,11 +164,12 @@ const AddressBlocksValidated = ({ scrollRef, shouldRender = true, isQueryEnabled
return (
+ >
+ { content }
+
);
};
diff --git a/ui/address/AddressContract.pw.tsx b/ui/address/AddressContract.pw.tsx
index 0229581a08..72f537af77 100644
--- a/ui/address/AddressContract.pw.tsx
+++ b/ui/address/AddressContract.pw.tsx
@@ -33,7 +33,7 @@ test.describe('ABI functionality', () => {
await expect(component.getByRole('button', { name: 'Connect wallet' })).toBeVisible();
await component.getByText('FLASHLOAN_PREMIUM_TOTAL').click();
- await expect(component.getByRole('button', { name: 'Read' })).toBeVisible();
+ await expect(component.getByLabel('FLASHLOAN_PREMIUM_TOTAL').getByRole('button', { name: 'Read' })).toBeVisible();
});
test('read, no wallet client', async({ render, createSocket, mockEnvs }) => {
@@ -49,7 +49,7 @@ test.describe('ABI functionality', () => {
await expect(component.getByRole('button', { name: 'Connect wallet' })).toBeHidden();
await component.getByText('FLASHLOAN_PREMIUM_TOTAL').click();
- await expect(component.getByRole('button', { name: 'Read' })).toBeVisible();
+ await expect(component.getByLabel('FLASHLOAN_PREMIUM_TOTAL').getByRole('button', { name: 'Read' })).toBeVisible();
});
test('write', async({ render, createSocket }) => {
diff --git a/ui/address/AddressContract.tsx b/ui/address/AddressContract.tsx
index a349532a7a..84c3337e8b 100644
--- a/ui/address/AddressContract.tsx
+++ b/ui/address/AddressContract.tsx
@@ -1,26 +1,22 @@
import React from 'react';
-import type { RoutedSubTab } from 'ui/shared/Tabs/types';
+import type { TabItemRegular } from 'toolkit/components/AdaptiveTabs/types';
-import RoutedTabs from 'ui/shared/Tabs/RoutedTabs';
+import RoutedTabs from 'toolkit/components/RoutedTabs/RoutedTabs';
interface Props {
- tabs: Array;
+ tabs: Array;
isLoading: boolean;
shouldRender?: boolean;
}
-const TAB_LIST_PROPS = {
- columnGap: 3,
-};
-
const AddressContract = ({ tabs, isLoading, shouldRender }: Props) => {
if (!shouldRender) {
return null;
}
return (
-
+
);
};
diff --git a/ui/address/AddressCsvExportLink.tsx b/ui/address/AddressCsvExportLink.tsx
index 13e470497f..a60e4cf961 100644
--- a/ui/address/AddressCsvExportLink.tsx
+++ b/ui/address/AddressCsvExportLink.tsx
@@ -1,4 +1,4 @@
-import { chakra, Tooltip, Hide, Flex } from '@chakra-ui/react';
+import { chakra, Flex } from '@chakra-ui/react';
import React from 'react';
import type { CsvExportParams } from 'types/client/address';
@@ -8,9 +8,10 @@ import { route } from 'nextjs-routes';
import config from 'configs/app';
import useIsInitialLoading from 'lib/hooks/useIsInitialLoading';
import useIsMobile from 'lib/hooks/useIsMobile';
-import Skeleton from 'ui/shared/chakra/Skeleton';
+import { Link } from 'toolkit/chakra/link';
+import { Skeleton } from 'toolkit/chakra/skeleton';
+import { Tooltip } from 'toolkit/chakra/tooltip';
import IconSvg from 'ui/shared/IconSvg';
-import LinkInternal from 'ui/shared/links/LinkInternal';
interface Props {
address: string;
@@ -30,27 +31,23 @@ const AddressCsvExportLink = ({ className, address, params, isLoading }: Props)
if (isInitialLoading) {
return (
-
-
-
-
+
+
);
}
return (
-
-
+
- Download CSV
-
+ Download CSV
+
);
};
diff --git a/ui/address/AddressDetails.tsx b/ui/address/AddressDetails.tsx
index 2adb3f5e9a..29ef454365 100644
--- a/ui/address/AddressDetails.tsx
+++ b/ui/address/AddressDetails.tsx
@@ -1,4 +1,4 @@
-import { Box, Text, Grid } from '@chakra-ui/react';
+import { Box, Text } from '@chakra-ui/react';
import { useRouter } from 'next/router';
import React from 'react';
@@ -12,8 +12,8 @@ import ServiceDegradationWarning from 'ui/shared/alerts/ServiceDegradationWarnin
import isCustomAppError from 'ui/shared/AppError/isCustomAppError';
import CopyToClipboard from 'ui/shared/CopyToClipboard';
import DataFetchAlert from 'ui/shared/DataFetchAlert';
-import * as DetailsInfoItem from 'ui/shared/DetailsInfoItem';
-import DetailsSponsoredItem from 'ui/shared/DetailsSponsoredItem';
+import * as DetailedInfo from 'ui/shared/DetailedInfo/DetailedInfo';
+import DetailedInfoSponsoredItem from 'ui/shared/DetailedInfo/DetailedInfoSponsoredItem';
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import BlockEntity from 'ui/shared/entities/block/BlockEntity';
import TxEntity from 'ui/shared/entities/tx/TxEntity';
@@ -31,10 +31,9 @@ import type { AddressQuery } from './utils/useAddressQuery';
interface Props {
addressQuery: AddressQuery;
- scrollRef?: React.RefObject;
}
-const AddressDetails = ({ addressQuery, scrollRef }: Props) => {
+const AddressDetails = ({ addressQuery }: Props) => {
const router = useRouter();
const addressHash = getQueryParamString(router.query.hash);
@@ -44,13 +43,6 @@ const AddressDetails = ({ addressQuery, scrollRef }: Props) => {
addressQuery,
});
- const handleCounterItemClick = React.useCallback(() => {
- window.setTimeout(() => {
- // cannot do scroll instantly, have to wait a little
- scrollRef?.current?.scrollIntoView({ behavior: 'smooth' });
- }, 500);
- }, [ scrollRef ]);
-
const error404Data = React.useMemo(() => ({
hash: addressHash || '',
is_contract: false,
@@ -94,54 +86,50 @@ const AddressDetails = ({ addressQuery, scrollRef }: Props) => {
return (
<>
{ addressQuery.isDegradedData && }
-
+
{ data.filecoin?.id && (
<>
-
ID
-
-
+
+
{ data.filecoin.id }
-
+
>
) }
{ data.filecoin?.actor_type && (
<>
-
Actor
-
-
+
+
-
+
>
) }
{ (data.filecoin?.actor_type === 'evm' || data.filecoin?.actor_type === 'ethaccount') && data?.filecoin?.robust && (
<>
-
Ethereum Address
-
-
+
+
-
+
>
) }
@@ -149,13 +137,13 @@ const AddressDetails = ({ addressQuery, scrollRef }: Props) => {
{ data.is_contract && data.creation_transaction_hash && (creatorAddressHash) && (
<>
-
Creator
-
-
+
+
{
/>
at txn
-
+
>
) }
- { data.is_contract && data.implementations && data.implementations?.length > 0 && (
+ { !addressQuery.isPlaceholderData && data.is_contract && data.implementations && data.implementations?.length > 0 && (
{
{ data.has_tokens && (
<>
-
Tokens
-
-
- { addressQuery.data ? : 0 }
-
+
+
+ { addressQuery.data ? : 0 }
+
>
) }
{ (config.features.multichainButton.isEnabled || (data.exchange_rate && data.has_tokens)) && (
<>
-
Net worth
-
-
+
+
-
+
>
)
}
-
Transactions
-
-
+
+
{ addressQuery.data ? (
) :
0 }
-
+
{ data.has_token_transfers && (
<>
-
Transfers
-
-
+
+
{ addressQuery.data ? (
) :
0 }
-
+
>
) }
{ countersQuery.data?.gas_usage_count && (
<>
-
Gas used
-
-
+
+
{ addressQuery.data ? (
@@ -273,53 +258,52 @@ const AddressDetails = ({ addressQuery, scrollRef }: Props) => {
address={ data.hash }
/>
) }
-
+
>
) }
{ data.has_validated_blocks && (
<>
-
{ `Blocks ${ getNetworkValidationActionText() }` }
-
-
+
+
{ addressQuery.data ? (
) :
0 }
-
+
>
) }
{ data.block_number_balance_updated_at && (
<>
-
Last balance update
-
-
+
+
-
+
>
) }
-
-
+
+
>
);
};
diff --git a/ui/address/AddressEpochRewards.tsx b/ui/address/AddressEpochRewards.tsx
index d97f586a0a..a29919cef4 100644
--- a/ui/address/AddressEpochRewards.tsx
+++ b/ui/address/AddressEpochRewards.tsx
@@ -1,4 +1,4 @@
-import { Hide, Show } from '@chakra-ui/react';
+import { Box } from '@chakra-ui/react';
import { useRouter } from 'next/router';
import React from 'react';
@@ -16,12 +16,11 @@ import AddressCsvExportLink from './AddressCsvExportLink';
import AddressEpochRewardsListItem from './epochRewards/AddressEpochRewardsListItem';
type Props = {
- scrollRef?: React.RefObject;
shouldRender?: boolean;
isQueryEnabled?: boolean;
};
-const AddressEpochRewards = ({ scrollRef, shouldRender = true, isQueryEnabled = true }: Props) => {
+const AddressEpochRewards = ({ shouldRender = true, isQueryEnabled = true }: Props) => {
const router = useRouter();
const isMounted = useIsMounted();
@@ -32,7 +31,6 @@ const AddressEpochRewards = ({ scrollRef, shouldRender = true, isQueryEnabled =
pathParams: {
hash,
},
- scrollRef,
options: {
enabled: isQueryEnabled && Boolean(hash),
placeholderData: generateListStub<'address_epoch_rewards'>(EPOCH_REWARD_ITEM, 50, { next_page_params: {
@@ -51,14 +49,14 @@ const AddressEpochRewards = ({ scrollRef, shouldRender = true, isQueryEnabled =
const content = rewardsQuery.data?.items ? (
<>
-
+
-
-
+
+
{ rewardsQuery.data.items.map((item, index) => (
)) }
-
+
>
) : null;
@@ -85,11 +83,12 @@ const AddressEpochRewards = ({ scrollRef, shouldRender = true, isQueryEnabled =
return (
+ >
+ { content }
+
);
};
diff --git a/ui/address/AddressInternalTxs.pw.tsx b/ui/address/AddressInternalTxs.pw.tsx
index cce88f4c99..c4401f1fec 100644
--- a/ui/address/AddressInternalTxs.pw.tsx
+++ b/ui/address/AddressInternalTxs.pw.tsx
@@ -14,6 +14,7 @@ const hooksConfig = {
};
test('base view +@mobile', async({ render, mockApiResponse }) => {
+ test.slow();
await mockApiResponse('address_internal_txs', internalTxsMock.baseResponse, { pathParams: { hash: ADDRESS_HASH } });
const component = await render(
@@ -21,5 +22,5 @@ test('base view +@mobile', async({ render, mockApiResponse }) => {
,
{ hooksConfig },
);
- await expect(component).toHaveScreenshot();
+ await expect(component).toHaveScreenshot({ timeout: 10_000 });
});
diff --git a/ui/address/AddressInternalTxs.tsx b/ui/address/AddressInternalTxs.tsx
index 259d623c42..f50950cff7 100644
--- a/ui/address/AddressInternalTxs.tsx
+++ b/ui/address/AddressInternalTxs.tsx
@@ -1,4 +1,4 @@
-import { Show, Hide } from '@chakra-ui/react';
+import { Box } from '@chakra-ui/react';
import { useRouter } from 'next/router';
import React from 'react';
@@ -24,11 +24,10 @@ import AddressTxsFilter from './AddressTxsFilter';
const getFilterValue = (getFilterValueFromQuery).bind(null, AddressFromToFilterValues);
type Props = {
- scrollRef?: React.RefObject;
shouldRender?: boolean;
isQueryEnabled?: boolean;
};
-const AddressInternalTxs = ({ scrollRef, shouldRender = true, isQueryEnabled = true }: Props) => {
+const AddressInternalTxs = ({ shouldRender = true, isQueryEnabled = true }: Props) => {
const router = useRouter();
const isMounted = useIsMounted();
@@ -40,7 +39,6 @@ const AddressInternalTxs = ({ scrollRef, shouldRender = true, isQueryEnabled = t
resourceName: 'address_internal_txs',
pathParams: { hash },
filters: { filter: filterValue },
- scrollRef,
options: {
enabled: isQueryEnabled,
placeholderData: generateListStub<'address_internal_txs'>(
@@ -70,19 +68,19 @@ const AddressInternalTxs = ({ scrollRef, shouldRender = true, isQueryEnabled = t
const content = data?.items ? (
<>
-
+
-
-
+
+
-
+
>
) : null ;
const actionBar = (
+ >
+ { content }
+
);
};
diff --git a/ui/address/AddressLogs.tsx b/ui/address/AddressLogs.tsx
index 9df130cd8d..9cb9e217f1 100644
--- a/ui/address/AddressLogs.tsx
+++ b/ui/address/AddressLogs.tsx
@@ -15,12 +15,11 @@ import AddressCsvExportLink from './AddressCsvExportLink';
import useAddressQuery from './utils/useAddressQuery';
type Props = {
- scrollRef?: React.RefObject;
shouldRender?: boolean;
isQueryEnabled?: boolean;
};
-const AddressLogs = ({ scrollRef, shouldRender = true, isQueryEnabled = true }: Props) => {
+const AddressLogs = ({ shouldRender = true, isQueryEnabled = true }: Props) => {
const router = useRouter();
const isMounted = useIsMounted();
@@ -28,7 +27,6 @@ const AddressLogs = ({ scrollRef, shouldRender = true, isQueryEnabled = true }:
const { data, isPlaceholderData, isError, pagination } = useQueryWithPages({
resourceName: 'address_logs',
pathParams: { hash },
- scrollRef,
options: {
enabled: isQueryEnabled,
placeholderData: generateListStub<'address_logs'>(LOG, 3, { next_page_params: {
@@ -70,11 +68,12 @@ const AddressLogs = ({ scrollRef, shouldRender = true, isQueryEnabled = true }:
return (
+ >
+ { content }
+
);
};
diff --git a/ui/address/AddressMud.tsx b/ui/address/AddressMud.tsx
index 93489f97fe..9c4df0ef7f 100644
--- a/ui/address/AddressMud.tsx
+++ b/ui/address/AddressMud.tsx
@@ -8,12 +8,11 @@ import AddressMudTable from './mud/AddressMudTable';
import AddressMudTables from './mud/AddressMudTables';
type Props = {
- scrollRef?: React.RefObject;
shouldRender?: boolean;
isQueryEnabled?: boolean;
};
-const AddressMud = ({ scrollRef, shouldRender = true, isQueryEnabled = true }: Props) => {
+const AddressMud = ({ shouldRender = true, isQueryEnabled = true }: Props) => {
const isMounted = useIsMounted();
const router = useRouter();
const tableId = router.query.table_id?.toString();
@@ -24,14 +23,14 @@ const AddressMud = ({ scrollRef, shouldRender = true, isQueryEnabled = true }: P
}
if (tableId && recordId) {
- return ;
+ return ;
}
if (tableId) {
- return ;
+ return ;
}
- return ;
+ return ;
};
export default AddressMud;
diff --git a/ui/address/AddressTokenTransfers.tsx b/ui/address/AddressTokenTransfers.tsx
index 077dc25b47..bdccc6d2b9 100644
--- a/ui/address/AddressTokenTransfers.tsx
+++ b/ui/address/AddressTokenTransfers.tsx
@@ -1,4 +1,4 @@
-import { Flex, Hide, Show, Text } from '@chakra-ui/react';
+import { Box, Flex, Text } from '@chakra-ui/react';
import { useQueryClient } from '@tanstack/react-query';
import { useRouter } from 'next/router';
import React from 'react';
@@ -63,14 +63,13 @@ const matchFilters = (filters: Filters, tokenTransfer: TokenTransfer, address?:
};
type Props = {
- scrollRef?: React.RefObject;
shouldRender?: boolean;
isQueryEnabled?: boolean;
// for tests only
overloadCount?: number;
};
-const AddressTokenTransfers = ({ scrollRef, overloadCount = OVERLOAD_COUNT, shouldRender = true, isQueryEnabled = true }: Props) => {
+const AddressTokenTransfers = ({ overloadCount = OVERLOAD_COUNT, shouldRender = true, isQueryEnabled = true }: Props) => {
const router = useRouter();
const queryClient = useQueryClient();
const isMobile = useIsMobile();
@@ -94,7 +93,6 @@ const AddressTokenTransfers = ({ scrollRef, overloadCount = OVERLOAD_COUNT, shou
resourceName: 'address_token_transfers',
pathParams: { hash: currentAddress },
filters: tokenFilter ? { token: tokenFilter } : filters,
- scrollRef,
options: {
enabled: isQueryEnabled,
placeholderData: getTokenTransfersStub(undefined, {
@@ -201,7 +199,7 @@ const AddressTokenTransfers = ({ scrollRef, overloadCount = OVERLOAD_COUNT, shou
const content = data?.items ? (
<>
-
+
-
-
+
+
{ pagination.page === 1 && !tokenFilter && (
-
+
>
) : null;
@@ -280,15 +278,16 @@ const AddressTokenTransfers = ({ scrollRef, overloadCount = OVERLOAD_COUNT, shou
return (
+ >
+ { content }
+
);
};
diff --git a/ui/address/AddressTokens.pw.tsx b/ui/address/AddressTokens.pw.tsx
index 3b2a34af8e..1d3802a1fd 100644
--- a/ui/address/AddressTokens.pw.tsx
+++ b/ui/address/AddressTokens.pw.tsx
@@ -144,7 +144,7 @@ test.describe('mobile', () => {
{ hooksConfig },
);
- await component.getByLabel('list').click();
+ await component.locator('button').filter({ hasText: 'List' }).click();
await expect(component).toHaveScreenshot();
});
diff --git a/ui/address/AddressTokens.tsx b/ui/address/AddressTokens.tsx
index 511c6c0079..a264995d58 100644
--- a/ui/address/AddressTokens.tsx
+++ b/ui/address/AddressTokens.tsx
@@ -1,4 +1,4 @@
-import { Box, HStack } from '@chakra-ui/react';
+import { Box, chakra, HStack } from '@chakra-ui/react';
import { useRouter } from 'next/router';
import React from 'react';
@@ -15,12 +15,13 @@ import getQueryParamString from 'lib/router/getQueryParamString';
import { NFT_TOKEN_TYPE_IDS } from 'lib/token/tokenTypes';
import { ADDRESS_TOKEN_BALANCE_ERC_20, ADDRESS_NFT_1155, ADDRESS_COLLECTION } from 'stubs/address';
import { generateListStub } from 'stubs/utils';
+import { Button, ButtonGroupRadio } from 'toolkit/chakra/button';
+import RoutedTabs from 'toolkit/components/RoutedTabs/RoutedTabs';
import PopoverFilter from 'ui/shared/filters/PopoverFilter';
import TokenTypeFilter from 'ui/shared/filters/TokenTypeFilter';
+import IconSvg from 'ui/shared/IconSvg';
import Pagination from 'ui/shared/pagination/Pagination';
import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages';
-import RadioButtonGroup from 'ui/shared/radioButtonGroup/RadioButtonGroup';
-import RoutedTabs from 'ui/shared/Tabs/RoutedTabs';
import AddressCollections from './tokens/AddressCollections';
import AddressNFTs from './tokens/AddressNFTs';
@@ -33,12 +34,10 @@ const TAB_LIST_PROPS = {
mt: 1,
mb: { base: 6, lg: 1 },
py: 5,
- columnGap: 3,
};
const TAB_LIST_PROPS_MOBILE = {
my: 8,
- columnGap: 3,
};
const getTokenFilterValue = (getFilterValuesFromQuery).bind(null, NFT_TOKEN_TYPE_IDS);
@@ -96,9 +95,9 @@ const AddressTokens = ({ shouldRender = true, isQueryEnabled = true }: Props) =>
filters: { type: tokenTypes },
});
- const handleNFTsDisplayTypeChange = React.useCallback((val: TNftDisplayType) => {
+ const handleNFTsDisplayTypeChange = React.useCallback((val: string) => {
cookies.set(cookies.NAMES.ADDRESS_NFT_DISPLAY_TYPE, val);
- setNftDisplayType(val);
+ setNftDisplayType(val as TNftDisplayType);
}, []);
const handleTokenTypesChange = React.useCallback((value: Array) => {
@@ -131,15 +130,20 @@ const AddressTokens = ({ shouldRender = true, isQueryEnabled = true }: Props) =>
];
const nftDisplayTypeRadio = (
-
- onChange={ handleNFTsDisplayTypeChange }
+
+ onChange={ handleNFTsDisplayTypeChange }
+ equalWidth
+ >
+
+
+ By collection
+
+
+
+ List
+
+
);
let pagination: PaginationParams | undefined;
@@ -158,7 +162,7 @@ const AddressTokens = ({ shouldRender = true, isQueryEnabled = true }: Props) =>
const rightSlot = (
<>
-
+
{ isNftTab && (hasNftData || hasActiveFilters) && nftDisplayTypeRadio }
{ isNftTab && (hasNftData || hasActiveFilters) && nftTypeFilter }
@@ -173,12 +177,11 @@ const AddressTokens = ({ shouldRender = true, isQueryEnabled = true }: Props) =>
>
diff --git a/ui/address/AddressTxs.pw.tsx b/ui/address/AddressTxs.pw.tsx
index 2f41a65ff7..6fa3830d71 100644
--- a/ui/address/AddressTxs.pw.tsx
+++ b/ui/address/AddressTxs.pw.tsx
@@ -41,7 +41,7 @@ test.describe('base view', () => {
);
});
- test('+@mobile', async() => {
+ test('desktop', async() => {
await expect(component).toHaveScreenshot();
});
@@ -55,6 +55,31 @@ test.describe('base view', () => {
});
});
+test.describe('base view', () => {
+ test.use({ viewport: pwConfig.viewport.mobile });
+
+ test('mobile', async({ render, mockApiResponse }) => {
+ await mockApiResponse(
+ 'address_txs',
+ {
+ items: [
+ txMock.base,
+ { ...txMock.base, hash: '0x62d597ebcf3e8d60096dd0363bc2f0f5e2df27ba1dacd696c51aa7c9409f3194' },
+ ],
+ next_page_params: DEFAULT_PAGINATION,
+ },
+ { pathParams: { hash: CURRENT_ADDRESS } },
+ );
+ const component = await render(
+
+
+ ,
+ { hooksConfig },
+ );
+ await expect(component).toHaveScreenshot();
+ });
+});
+
test.describe('socket', () => {
// FIXME
// test cases which use socket cannot run in parallel since the socket server always run on the same port
diff --git a/ui/address/AddressTxs.tsx b/ui/address/AddressTxs.tsx
index 4df86bd5b7..dcd1b46ae8 100644
--- a/ui/address/AddressTxs.tsx
+++ b/ui/address/AddressTxs.tsx
@@ -47,33 +47,32 @@ const matchFilter = (filterValue: AddressFromToFilter, transaction: Transaction,
};
type Props = {
- scrollRef?: React.RefObject;
shouldRender?: boolean;
isQueryEnabled?: boolean;
// for tests only
overloadCount?: number;
};
-const AddressTxs = ({ scrollRef, overloadCount = OVERLOAD_COUNT, shouldRender = true, isQueryEnabled = true }: Props) => {
+const AddressTxs = ({ overloadCount = OVERLOAD_COUNT, shouldRender = true, isQueryEnabled = true }: Props) => {
const router = useRouter();
const queryClient = useQueryClient();
const isMounted = useIsMounted();
const [ socketAlert, setSocketAlert ] = React.useState('');
const [ newItemsCount, setNewItemsCount ] = React.useState(0);
- const [ sort, setSort ] = React.useState(getSortValueFromQuery(router.query, SORT_OPTIONS));
+ const [ sort, setSort ] = React.useState(getSortValueFromQuery(router.query, SORT_OPTIONS) || 'default');
const isMobile = useIsMobile();
const currentAddress = getQueryParamString(router.query.hash);
- const [ filterValue, setFilterValue ] = React.useState(getFilterValue(router.query.filter));
+ const initialFilterValue = getFilterValue(router.query.filter);
+ const [ filterValue, setFilterValue ] = React.useState(initialFilterValue);
const addressTxsQuery = useQueryWithPages({
resourceName: 'address_txs',
pathParams: { hash: currentAddress },
filters: { filter: filterValue },
sorting: getSortParamsFromValue(sort),
- scrollRef,
options: {
enabled: isQueryEnabled,
placeholderData: generateListStub<'address_txs'>(TX, 50, { next_page_params: {
@@ -167,7 +166,7 @@ const AddressTxs = ({ scrollRef, overloadCount = OVERLOAD_COUNT, shouldRender =
const filter = (
{ !isMobile && (
-
+
{ filter }
{ currentAddress && csvExportLink }
diff --git a/ui/address/AddressTxsFilter.tsx b/ui/address/AddressTxsFilter.tsx
index d3bdbd61fe..0d452770cf 100644
--- a/ui/address/AddressTxsFilter.tsx
+++ b/ui/address/AddressTxsFilter.tsx
@@ -1,3 +1,4 @@
+import { createListCollection } from '@chakra-ui/react';
import React from 'react';
import type { AddressFromToFilter } from 'types/api/address';
@@ -10,25 +11,26 @@ const OPTIONS = [
{ value: 'from', label: 'Outgoing transactions' },
{ value: 'to', label: 'Incoming transactions' },
];
+const collection = createListCollection({ items: OPTIONS });
interface Props {
hasActiveFilter: boolean;
- defaultFilter: AddressFromToFilter;
+ initialValue: AddressFromToFilter;
onFilterChange: (nextValue: string | Array) => void;
isLoading?: boolean;
}
-const AddressTxsFilter = ({ onFilterChange, defaultFilter, hasActiveFilter, isLoading }: Props) => {
+const AddressTxsFilter = ({ onFilterChange, initialValue, hasActiveFilter, isLoading }: Props) => {
const isInitialLoading = useIsInitialLoading(isLoading);
return (
);
};
diff --git a/ui/address/AddressWithdrawals.tsx b/ui/address/AddressWithdrawals.tsx
index 414a016a25..30a88a1693 100644
--- a/ui/address/AddressWithdrawals.tsx
+++ b/ui/address/AddressWithdrawals.tsx
@@ -1,4 +1,4 @@
-import { Show, Hide } from '@chakra-ui/react';
+import { Box } from '@chakra-ui/react';
import { useRouter } from 'next/router';
import React from 'react';
@@ -14,11 +14,10 @@ import BeaconChainWithdrawalsListItem from 'ui/withdrawals/beaconChain/BeaconCha
import BeaconChainWithdrawalsTable from 'ui/withdrawals/beaconChain/BeaconChainWithdrawalsTable';
type Props = {
- scrollRef?: React.RefObject;
shouldRender?: boolean;
isQueryEnabled?: boolean;
};
-const AddressWithdrawals = ({ scrollRef, shouldRender = true, isQueryEnabled = true }: Props) => {
+const AddressWithdrawals = ({ shouldRender = true, isQueryEnabled = true }: Props) => {
const router = useRouter();
const isMounted = useIsMounted();
@@ -27,7 +26,6 @@ const AddressWithdrawals = ({ scrollRef, shouldRender = true, isQueryEnabled = t
const { data, isPlaceholderData, isError, pagination } = useQueryWithPages({
resourceName: 'address_withdrawals',
pathParams: { hash },
- scrollRef,
options: {
enabled: isQueryEnabled,
placeholderData: generateListStub<'address_withdrawals'>(WITHDRAWAL, 50, { next_page_params: {
@@ -43,7 +41,7 @@ const AddressWithdrawals = ({ scrollRef, shouldRender = true, isQueryEnabled = t
const content = data?.items ? (
<>
-
+
{ data.items.map((item, index) => (
)) }
-
-
+
+
-
+
>
) : null ;
@@ -73,11 +71,12 @@ const AddressWithdrawals = ({ scrollRef, shouldRender = true, isQueryEnabled = t
return (
+ >
+ { content }
+
);
};
diff --git a/ui/address/SolidityscanReport.tsx b/ui/address/SolidityscanReport.tsx
index a704c53a1a..5e8d29908d 100644
--- a/ui/address/SolidityscanReport.tsx
+++ b/ui/address/SolidityscanReport.tsx
@@ -1,4 +1,4 @@
-import { Box, Text, Icon, PopoverTrigger, PopoverContent, PopoverBody, useDisclosure } from '@chakra-ui/react';
+import { Box, Text, Icon } from '@chakra-ui/react';
import React from 'react';
// This icon doesn't work properly when it is in the sprite
@@ -6,8 +6,8 @@ import React from 'react';
// eslint-disable-next-line no-restricted-imports
import solidityScanIcon from 'icons/brands/solidity_scan.svg';
import useFetchReport from 'lib/solidityScan/useFetchReport';
-import Popover from 'ui/shared/chakra/Popover';
-import LinkExternal from 'ui/shared/links/LinkExternal';
+import { Link } from 'toolkit/chakra/link';
+import { PopoverBody, PopoverContent, PopoverRoot } from 'toolkit/chakra/popover';
import SolidityscanReportButton from 'ui/shared/solidityscanReport/SolidityscanReportButton';
import SolidityscanReportDetails from 'ui/shared/solidityscanReport/SolidityscanReportDetails';
import SolidityscanReportScore from 'ui/shared/solidityscanReport/SolidityscanReportScore';
@@ -17,7 +17,6 @@ interface Props {
}
const SolidityscanReport = ({ hash }: Props) => {
- const { isOpen, onToggle, onClose } = useDisclosure();
const { data, isPlaceholderData, isError } = useFetchReport({ hash });
@@ -36,17 +35,13 @@ const SolidityscanReport = ({ hash }: Props) => {
const vulnerabilitiesCount = vulnerabilitiesCounts.reduce((acc, val) => acc + val, 0);
return (
-
-
-
-
+
+
-
+
Contract analyzed for 240+ vulnerability patterns by
@@ -55,14 +50,14 @@ const SolidityscanReport = ({ hash }: Props) => {
{ vulnerabilities && vulnerabilitiesCount > 0 && (
- Vulnerabilities distribution
+ Vulnerabilities distribution
) }
- View full report
+ View full report
-
+
);
};
diff --git a/ui/address/__screenshots__/AddressCoinBalance.pw.tsx_dark-color-mode_base-view-dark-mode-1.png b/ui/address/__screenshots__/AddressCoinBalance.pw.tsx_dark-color-mode_base-view-dark-mode-1.png
index 58ccb93f59..812327ec8c 100644
Binary files a/ui/address/__screenshots__/AddressCoinBalance.pw.tsx_dark-color-mode_base-view-dark-mode-1.png and b/ui/address/__screenshots__/AddressCoinBalance.pw.tsx_dark-color-mode_base-view-dark-mode-1.png differ
diff --git a/ui/address/__screenshots__/AddressCoinBalance.pw.tsx_default_base-view-dark-mode-1.png b/ui/address/__screenshots__/AddressCoinBalance.pw.tsx_default_base-view-dark-mode-1.png
index ef39e74b09..e60254423e 100644
Binary files a/ui/address/__screenshots__/AddressCoinBalance.pw.tsx_default_base-view-dark-mode-1.png and b/ui/address/__screenshots__/AddressCoinBalance.pw.tsx_default_base-view-dark-mode-1.png differ
diff --git a/ui/address/__screenshots__/AddressCoinBalance.pw.tsx_default_mobile-base-view-1.png b/ui/address/__screenshots__/AddressCoinBalance.pw.tsx_default_mobile-base-view-1.png
index 6115f79ad2..63f8aa7e76 100644
Binary files a/ui/address/__screenshots__/AddressCoinBalance.pw.tsx_default_mobile-base-view-1.png and b/ui/address/__screenshots__/AddressCoinBalance.pw.tsx_default_mobile-base-view-1.png differ
diff --git a/ui/address/__screenshots__/AddressDetails.pw.tsx_default_mobile-contract-1.png b/ui/address/__screenshots__/AddressDetails.pw.tsx_default_mobile-contract-1.png
index 7c160310cf..5f3e2d0098 100644
Binary files a/ui/address/__screenshots__/AddressDetails.pw.tsx_default_mobile-contract-1.png and b/ui/address/__screenshots__/AddressDetails.pw.tsx_default_mobile-contract-1.png differ
diff --git a/ui/address/__screenshots__/AddressDetails.pw.tsx_default_mobile-filecoin-1.png b/ui/address/__screenshots__/AddressDetails.pw.tsx_default_mobile-filecoin-1.png
index 6f79f2dd68..9e9004e879 100644
Binary files a/ui/address/__screenshots__/AddressDetails.pw.tsx_default_mobile-filecoin-1.png and b/ui/address/__screenshots__/AddressDetails.pw.tsx_default_mobile-filecoin-1.png differ
diff --git a/ui/address/__screenshots__/AddressDetails.pw.tsx_default_mobile-validator-1.png b/ui/address/__screenshots__/AddressDetails.pw.tsx_default_mobile-validator-1.png
index fdc0888b51..12132c9c88 100644
Binary files a/ui/address/__screenshots__/AddressDetails.pw.tsx_default_mobile-validator-1.png and b/ui/address/__screenshots__/AddressDetails.pw.tsx_default_mobile-validator-1.png differ
diff --git a/ui/address/__screenshots__/AddressEpochRewards.pw.tsx_default_base-view-mobile-1.png b/ui/address/__screenshots__/AddressEpochRewards.pw.tsx_default_base-view-mobile-1.png
index a29982c419..79ab546eba 100644
Binary files a/ui/address/__screenshots__/AddressEpochRewards.pw.tsx_default_base-view-mobile-1.png and b/ui/address/__screenshots__/AddressEpochRewards.pw.tsx_default_base-view-mobile-1.png differ
diff --git a/ui/address/__screenshots__/AddressEpochRewards.pw.tsx_mobile_base-view-mobile-1.png b/ui/address/__screenshots__/AddressEpochRewards.pw.tsx_mobile_base-view-mobile-1.png
index 99d0b36bc9..360ded8be4 100644
Binary files a/ui/address/__screenshots__/AddressEpochRewards.pw.tsx_mobile_base-view-mobile-1.png and b/ui/address/__screenshots__/AddressEpochRewards.pw.tsx_mobile_base-view-mobile-1.png differ
diff --git a/ui/address/__screenshots__/AddressInternalTxs.pw.tsx_default_base-view-mobile-1.png b/ui/address/__screenshots__/AddressInternalTxs.pw.tsx_default_base-view-mobile-1.png
index 0d64c97a27..dc4fa82980 100644
Binary files a/ui/address/__screenshots__/AddressInternalTxs.pw.tsx_default_base-view-mobile-1.png and b/ui/address/__screenshots__/AddressInternalTxs.pw.tsx_default_base-view-mobile-1.png differ
diff --git a/ui/address/__screenshots__/AddressInternalTxs.pw.tsx_mobile_base-view-mobile-1.png b/ui/address/__screenshots__/AddressInternalTxs.pw.tsx_mobile_base-view-mobile-1.png
index ab937a3be6..a30e284f3f 100644
Binary files a/ui/address/__screenshots__/AddressInternalTxs.pw.tsx_mobile_base-view-mobile-1.png and b/ui/address/__screenshots__/AddressInternalTxs.pw.tsx_mobile_base-view-mobile-1.png differ
diff --git a/ui/address/__screenshots__/AddressTokenTransfers.pw.tsx_default_mobile-with-token-filter-and-no-pagination-1.png b/ui/address/__screenshots__/AddressTokenTransfers.pw.tsx_default_mobile-with-token-filter-and-no-pagination-1.png
index 9c47d83b6a..690d82cbc2 100644
Binary files a/ui/address/__screenshots__/AddressTokenTransfers.pw.tsx_default_mobile-with-token-filter-and-no-pagination-1.png and b/ui/address/__screenshots__/AddressTokenTransfers.pw.tsx_default_mobile-with-token-filter-and-no-pagination-1.png differ
diff --git a/ui/address/__screenshots__/AddressTokenTransfers.pw.tsx_default_mobile-with-token-filter-and-pagination-1.png b/ui/address/__screenshots__/AddressTokenTransfers.pw.tsx_default_mobile-with-token-filter-and-pagination-1.png
index ce20551d45..39b7b891ff 100644
Binary files a/ui/address/__screenshots__/AddressTokenTransfers.pw.tsx_default_mobile-with-token-filter-and-pagination-1.png and b/ui/address/__screenshots__/AddressTokenTransfers.pw.tsx_default_mobile-with-token-filter-and-pagination-1.png differ
diff --git a/ui/address/__screenshots__/AddressTokenTransfers.pw.tsx_default_with-token-filter-and-no-pagination-1.png b/ui/address/__screenshots__/AddressTokenTransfers.pw.tsx_default_with-token-filter-and-no-pagination-1.png
index 5387be0020..2a062b77a3 100644
Binary files a/ui/address/__screenshots__/AddressTokenTransfers.pw.tsx_default_with-token-filter-and-no-pagination-1.png and b/ui/address/__screenshots__/AddressTokenTransfers.pw.tsx_default_with-token-filter-and-no-pagination-1.png differ
diff --git a/ui/address/__screenshots__/AddressTokenTransfers.pw.tsx_default_with-token-filter-and-pagination-1.png b/ui/address/__screenshots__/AddressTokenTransfers.pw.tsx_default_with-token-filter-and-pagination-1.png
index 229ab9a91b..5902823332 100644
Binary files a/ui/address/__screenshots__/AddressTokenTransfers.pw.tsx_default_with-token-filter-and-pagination-1.png and b/ui/address/__screenshots__/AddressTokenTransfers.pw.tsx_default_with-token-filter-and-pagination-1.png differ
diff --git a/ui/address/__screenshots__/AddressTokens.pw.tsx_dark-color-mode_collections-dark-mode-1.png b/ui/address/__screenshots__/AddressTokens.pw.tsx_dark-color-mode_collections-dark-mode-1.png
index 63a64f592c..f8c313663e 100644
Binary files a/ui/address/__screenshots__/AddressTokens.pw.tsx_dark-color-mode_collections-dark-mode-1.png and b/ui/address/__screenshots__/AddressTokens.pw.tsx_dark-color-mode_collections-dark-mode-1.png differ
diff --git a/ui/address/__screenshots__/AddressTokens.pw.tsx_dark-color-mode_erc20-dark-mode-1.png b/ui/address/__screenshots__/AddressTokens.pw.tsx_dark-color-mode_erc20-dark-mode-1.png
index 3d27aa37c1..43731b96ce 100644
Binary files a/ui/address/__screenshots__/AddressTokens.pw.tsx_dark-color-mode_erc20-dark-mode-1.png and b/ui/address/__screenshots__/AddressTokens.pw.tsx_dark-color-mode_erc20-dark-mode-1.png differ
diff --git a/ui/address/__screenshots__/AddressTokens.pw.tsx_dark-color-mode_nfts-dark-mode-1.png b/ui/address/__screenshots__/AddressTokens.pw.tsx_dark-color-mode_nfts-dark-mode-1.png
index 9aaaf6dfac..54fe2ae04d 100644
Binary files a/ui/address/__screenshots__/AddressTokens.pw.tsx_dark-color-mode_nfts-dark-mode-1.png and b/ui/address/__screenshots__/AddressTokens.pw.tsx_dark-color-mode_nfts-dark-mode-1.png differ
diff --git a/ui/address/__screenshots__/AddressTokens.pw.tsx_default_collections-dark-mode-1.png b/ui/address/__screenshots__/AddressTokens.pw.tsx_default_collections-dark-mode-1.png
index ea5d4ec1cc..b41c5af823 100644
Binary files a/ui/address/__screenshots__/AddressTokens.pw.tsx_default_collections-dark-mode-1.png and b/ui/address/__screenshots__/AddressTokens.pw.tsx_default_collections-dark-mode-1.png differ
diff --git a/ui/address/__screenshots__/AddressTokens.pw.tsx_default_erc20-dark-mode-1.png b/ui/address/__screenshots__/AddressTokens.pw.tsx_default_erc20-dark-mode-1.png
index 55792ab34d..8caf660dff 100644
Binary files a/ui/address/__screenshots__/AddressTokens.pw.tsx_default_erc20-dark-mode-1.png and b/ui/address/__screenshots__/AddressTokens.pw.tsx_default_erc20-dark-mode-1.png differ
diff --git a/ui/address/__screenshots__/AddressTokens.pw.tsx_default_mobile-collections-1.png b/ui/address/__screenshots__/AddressTokens.pw.tsx_default_mobile-collections-1.png
index af1b440782..3786f5643f 100644
Binary files a/ui/address/__screenshots__/AddressTokens.pw.tsx_default_mobile-collections-1.png and b/ui/address/__screenshots__/AddressTokens.pw.tsx_default_mobile-collections-1.png differ
diff --git a/ui/address/__screenshots__/AddressTokens.pw.tsx_default_mobile-erc20-1.png b/ui/address/__screenshots__/AddressTokens.pw.tsx_default_mobile-erc20-1.png
index 716c9835f5..3703579f96 100644
Binary files a/ui/address/__screenshots__/AddressTokens.pw.tsx_default_mobile-erc20-1.png and b/ui/address/__screenshots__/AddressTokens.pw.tsx_default_mobile-erc20-1.png differ
diff --git a/ui/address/__screenshots__/AddressTokens.pw.tsx_default_mobile-nfts-1.png b/ui/address/__screenshots__/AddressTokens.pw.tsx_default_mobile-nfts-1.png
index 17132ec07c..ec833181f4 100644
Binary files a/ui/address/__screenshots__/AddressTokens.pw.tsx_default_mobile-nfts-1.png and b/ui/address/__screenshots__/AddressTokens.pw.tsx_default_mobile-nfts-1.png differ
diff --git a/ui/address/__screenshots__/AddressTokens.pw.tsx_default_nfts-dark-mode-1.png b/ui/address/__screenshots__/AddressTokens.pw.tsx_default_nfts-dark-mode-1.png
index 76e318c99b..6dbc68727b 100644
Binary files a/ui/address/__screenshots__/AddressTokens.pw.tsx_default_nfts-dark-mode-1.png and b/ui/address/__screenshots__/AddressTokens.pw.tsx_default_nfts-dark-mode-1.png differ
diff --git a/ui/address/__screenshots__/AddressTokens.pw.tsx_default_update-balances-via-socket-base-flow-1.png b/ui/address/__screenshots__/AddressTokens.pw.tsx_default_update-balances-via-socket-base-flow-1.png
index 5c05da9ea0..8cf44452a3 100644
Binary files a/ui/address/__screenshots__/AddressTokens.pw.tsx_default_update-balances-via-socket-base-flow-1.png and b/ui/address/__screenshots__/AddressTokens.pw.tsx_default_update-balances-via-socket-base-flow-1.png differ
diff --git a/ui/address/__screenshots__/AddressTokens.pw.tsx_default_update-balances-via-socket-base-flow-2.png b/ui/address/__screenshots__/AddressTokens.pw.tsx_default_update-balances-via-socket-base-flow-2.png
index 304c1422a2..7eb6f7b652 100644
Binary files a/ui/address/__screenshots__/AddressTokens.pw.tsx_default_update-balances-via-socket-base-flow-2.png and b/ui/address/__screenshots__/AddressTokens.pw.tsx_default_update-balances-via-socket-base-flow-2.png differ
diff --git a/ui/address/__screenshots__/AddressTxs.pw.tsx_default_base-view-desktop-1.png b/ui/address/__screenshots__/AddressTxs.pw.tsx_default_base-view-desktop-1.png
new file mode 100644
index 0000000000..8790543f17
Binary files /dev/null and b/ui/address/__screenshots__/AddressTxs.pw.tsx_default_base-view-desktop-1.png differ
diff --git a/ui/address/__screenshots__/AddressTxs.pw.tsx_default_base-view-mobile-1.png b/ui/address/__screenshots__/AddressTxs.pw.tsx_default_base-view-mobile-1.png
index 9138f10c12..d64e9a3525 100644
Binary files a/ui/address/__screenshots__/AddressTxs.pw.tsx_default_base-view-mobile-1.png and b/ui/address/__screenshots__/AddressTxs.pw.tsx_default_base-view-mobile-1.png differ
diff --git a/ui/address/__screenshots__/AddressTxs.pw.tsx_default_base-view-screen-xl-base-view-1.png b/ui/address/__screenshots__/AddressTxs.pw.tsx_default_base-view-screen-xl-base-view-1.png
index 19ddbcbbff..3fdc25082f 100644
Binary files a/ui/address/__screenshots__/AddressTxs.pw.tsx_default_base-view-screen-xl-base-view-1.png and b/ui/address/__screenshots__/AddressTxs.pw.tsx_default_base-view-screen-xl-base-view-1.png differ
diff --git a/ui/address/__screenshots__/AddressTxs.pw.tsx_mobile_base-view-mobile-1.png b/ui/address/__screenshots__/AddressTxs.pw.tsx_mobile_base-view-mobile-1.png
deleted file mode 100644
index c4f406ff15..0000000000
Binary files a/ui/address/__screenshots__/AddressTxs.pw.tsx_mobile_base-view-mobile-1.png and /dev/null differ
diff --git a/ui/address/__screenshots__/SolidityscanReport.pw.tsx_dark-color-mode_average-report-dark-mode-mobile-2.png b/ui/address/__screenshots__/SolidityscanReport.pw.tsx_dark-color-mode_average-report-dark-mode-mobile-2.png
index b2350c3805..73f6e12049 100644
Binary files a/ui/address/__screenshots__/SolidityscanReport.pw.tsx_dark-color-mode_average-report-dark-mode-mobile-2.png and b/ui/address/__screenshots__/SolidityscanReport.pw.tsx_dark-color-mode_average-report-dark-mode-mobile-2.png differ
diff --git a/ui/address/__screenshots__/SolidityscanReport.pw.tsx_default_average-report-dark-mode-mobile-2.png b/ui/address/__screenshots__/SolidityscanReport.pw.tsx_default_average-report-dark-mode-mobile-2.png
index bb83eb74a5..c34672a8de 100644
Binary files a/ui/address/__screenshots__/SolidityscanReport.pw.tsx_default_average-report-dark-mode-mobile-2.png and b/ui/address/__screenshots__/SolidityscanReport.pw.tsx_default_average-report-dark-mode-mobile-2.png differ
diff --git a/ui/address/__screenshots__/SolidityscanReport.pw.tsx_default_great-report-2.png b/ui/address/__screenshots__/SolidityscanReport.pw.tsx_default_great-report-2.png
index e6da9182a9..02781e3826 100644
Binary files a/ui/address/__screenshots__/SolidityscanReport.pw.tsx_default_great-report-2.png and b/ui/address/__screenshots__/SolidityscanReport.pw.tsx_default_great-report-2.png differ
diff --git a/ui/address/__screenshots__/SolidityscanReport.pw.tsx_default_low-report-2.png b/ui/address/__screenshots__/SolidityscanReport.pw.tsx_default_low-report-2.png
index 92ac51c72b..a5fbc9d71f 100644
Binary files a/ui/address/__screenshots__/SolidityscanReport.pw.tsx_default_low-report-2.png and b/ui/address/__screenshots__/SolidityscanReport.pw.tsx_default_low-report-2.png differ
diff --git a/ui/address/__screenshots__/SolidityscanReport.pw.tsx_mobile_average-report-dark-mode-mobile-2.png b/ui/address/__screenshots__/SolidityscanReport.pw.tsx_mobile_average-report-dark-mode-mobile-2.png
index 6380efd335..b79fd34987 100644
Binary files a/ui/address/__screenshots__/SolidityscanReport.pw.tsx_mobile_average-report-dark-mode-mobile-2.png and b/ui/address/__screenshots__/SolidityscanReport.pw.tsx_mobile_average-report-dark-mode-mobile-2.png differ
diff --git a/ui/address/accountHistory/AddressAccountHistoryListItem.tsx b/ui/address/accountHistory/AddressAccountHistoryListItem.tsx
index 55dc187e5a..a4073c2506 100644
--- a/ui/address/accountHistory/AddressAccountHistoryListItem.tsx
+++ b/ui/address/accountHistory/AddressAccountHistoryListItem.tsx
@@ -3,9 +3,9 @@ import React, { useMemo } from 'react';
import type { NovesResponseData } from 'types/api/noves';
-import Skeleton from 'ui/shared/chakra/Skeleton';
+import { Link } from 'toolkit/chakra/link';
+import { Skeleton } from 'toolkit/chakra/skeleton';
import IconSvg from 'ui/shared/IconSvg';
-import LinkInternal from 'ui/shared/links/LinkInternal';
import ListItemMobile from 'ui/shared/ListItemMobile/ListItemMobile';
import NovesFromTo from 'ui/shared/Noves/NovesFromTo';
import TimeAgoWithTooltip from 'ui/shared/TimeAgoWithTooltip';
@@ -26,7 +26,7 @@ const AddressAccountHistoryListItem = (props: Props) => {
return (
-
+
{
-
-
+
{ parsedDescription }
-
+
diff --git a/ui/address/accountHistory/AddressAccountHistoryTableItem.tsx b/ui/address/accountHistory/AddressAccountHistoryTableItem.tsx
index 23bae973c9..bc583c3728 100644
--- a/ui/address/accountHistory/AddressAccountHistoryTableItem.tsx
+++ b/ui/address/accountHistory/AddressAccountHistoryTableItem.tsx
@@ -1,11 +1,12 @@
-import { Td, Tr, Box } from '@chakra-ui/react';
+import { Box } from '@chakra-ui/react';
import React, { useMemo } from 'react';
import type { NovesResponseData } from 'types/api/noves';
-import Skeleton from 'ui/shared/chakra/Skeleton';
+import { Link } from 'toolkit/chakra/link';
+import { Skeleton } from 'toolkit/chakra/skeleton';
+import { TableCell, TableRow } from 'toolkit/chakra/table';
import IconSvg from 'ui/shared/IconSvg';
-import LinkInternal from 'ui/shared/links/LinkInternal';
import NovesFromTo from 'ui/shared/Noves/NovesFromTo';
import TimeAgoWithTooltip from 'ui/shared/TimeAgoWithTooltip';
@@ -24,18 +25,18 @@ const AddressAccountHistoryTableItem = (props: Props) => {
}, [ props.tx.classificationData.description ]);
return (
-
-
+
+
-
-
-
+
+
+
{
_dark={{ color: 'gray.400' }}
/>
-
{ parsedDescription }
-
+
-
-
+
+
-
-
+
+
);
};
diff --git a/ui/address/blocksValidated/AddressBlocksValidatedListItem.tsx b/ui/address/blocksValidated/AddressBlocksValidatedListItem.tsx
index c1cb7bd75e..bded972a24 100644
--- a/ui/address/blocksValidated/AddressBlocksValidatedListItem.tsx
+++ b/ui/address/blocksValidated/AddressBlocksValidatedListItem.tsx
@@ -7,8 +7,8 @@ import type { Block } from 'types/api/block';
import config from 'configs/app';
import getBlockTotalReward from 'lib/block/getBlockTotalReward';
import { currencyUnits } from 'lib/units';
+import { Skeleton } from 'toolkit/chakra/skeleton';
import BlockGasUsed from 'ui/shared/block/BlockGasUsed';
-import Skeleton from 'ui/shared/chakra/Skeleton';
import BlockEntity from 'ui/shared/entities/block/BlockEntity';
import ListItemMobile from 'ui/shared/ListItemMobile/ListItemMobile';
import TimeAgoWithTooltip from 'ui/shared/TimeAgoWithTooltip';
@@ -22,7 +22,7 @@ const AddressBlocksValidatedListItem = (props: Props) => {
const totalReward = getBlockTotalReward(props);
return (
-
+
{
timestamp={ props.timestamp }
enableIncrement={ props.page === 1 }
isLoading={ props.isLoading }
- color="text_secondary"
+ color="text.secondary"
display="inline-block"
/>
- Txn
-
+ Txn
+
{ props.transaction_count }
- Gas used
-
- { BigNumber(props.gas_used || 0).toFormat() }
+ Gas used
+
+ { BigNumber(props.gas_used || 0).toFormat() }
{ !config.UI.views.block.hiddenFields?.total_reward && !config.features.rollup.isEnabled && (
- Reward { currencyUnits.ether }
-
- { totalReward.toFixed() }
+ Reward { currencyUnits.ether }
+
+ { totalReward.toFixed() }
) }
diff --git a/ui/address/blocksValidated/AddressBlocksValidatedTableItem.tsx b/ui/address/blocksValidated/AddressBlocksValidatedTableItem.tsx
index 6427902394..fc2a5c45b5 100644
--- a/ui/address/blocksValidated/AddressBlocksValidatedTableItem.tsx
+++ b/ui/address/blocksValidated/AddressBlocksValidatedTableItem.tsx
@@ -1,4 +1,4 @@
-import { Td, Tr, Flex } from '@chakra-ui/react';
+import { Flex } from '@chakra-ui/react';
import BigNumber from 'bignumber.js';
import React from 'react';
@@ -6,8 +6,9 @@ import type { Block } from 'types/api/block';
import config from 'configs/app';
import getBlockTotalReward from 'lib/block/getBlockTotalReward';
+import { Skeleton } from 'toolkit/chakra/skeleton';
+import { TableCell, TableRow } from 'toolkit/chakra/table';
import BlockGasUsed from 'ui/shared/block/BlockGasUsed';
-import Skeleton from 'ui/shared/chakra/Skeleton';
import BlockEntity from 'ui/shared/entities/block/BlockEntity';
import TimeAgoWithTooltip from 'ui/shared/TimeAgoWithTooltip';
@@ -20,51 +21,50 @@ const AddressBlocksValidatedTableItem = (props: Props) => {
const totalReward = getBlockTotalReward(props);
return (
-
-
+
+
-
-
+
+
-
-
-
+
+
+
{ props.transaction_count }
-
-
+
+
-
+
{ BigNumber(props.gas_used || 0).toFormat() }
-
+
{ !config.UI.views.block.hiddenFields?.total_reward && !config.features.rollup.isEnabled && (
-
-
+
+
{ totalReward.toFixed() }
-
+
) }
-
+
);
};
diff --git a/ui/address/coinBalance/AddressCoinBalanceChart.tsx b/ui/address/coinBalance/AddressCoinBalanceChart.tsx
index 94542df368..c51b410891 100644
--- a/ui/address/coinBalance/AddressCoinBalanceChart.tsx
+++ b/ui/address/coinBalance/AddressCoinBalanceChart.tsx
@@ -34,7 +34,7 @@ const AddressCoinBalanceChart = ({ addressHash }: Props) => {
isLoading={ isPending }
h="300px"
units={ currencyUnits.ether }
- emptyText={ data?.days && `Insufficient data for the past ${ data.days } days` }
+ emptyText={ data?.days ? `Insufficient data for the past ${ data.days } days` : undefined }
/>
);
};
diff --git a/ui/address/coinBalance/AddressCoinBalanceHistory.tsx b/ui/address/coinBalance/AddressCoinBalanceHistory.tsx
index f084e92654..8e179f7cd0 100644
--- a/ui/address/coinBalance/AddressCoinBalanceHistory.tsx
+++ b/ui/address/coinBalance/AddressCoinBalanceHistory.tsx
@@ -1,4 +1,4 @@
-import { Hide, Show, Table, Tbody, Th, Tr } from '@chakra-ui/react';
+import { Box } from '@chakra-ui/react';
import type { UseQueryResult } from '@tanstack/react-query';
import React from 'react';
@@ -7,10 +7,10 @@ import type { PaginationParams } from 'ui/shared/pagination/types';
import type { ResourceError } from 'lib/api/resources';
import { currencyUnits } from 'lib/units';
+import { TableBody, TableColumnHeader, TableHeaderSticky, TableRoot, TableRow } from 'toolkit/chakra/table';
import ActionBar, { ACTION_BAR_HEIGHT_DESKTOP } from 'ui/shared/ActionBar';
import DataListDisplay from 'ui/shared/DataListDisplay';
import Pagination from 'ui/shared/pagination/Pagination';
-import { default as Thead } from 'ui/shared/TheadSticky';
import AddressCoinBalanceListItem from './AddressCoinBalanceListItem';
import AddressCoinBalanceTableItem from './AddressCoinBalanceTableItem';
@@ -25,18 +25,18 @@ const AddressCoinBalanceHistory = ({ query }: Props) => {
const content = query.data?.items ? (
<>
-
-
-
-
- Block
- Txn
- Age
- Balance { currencyUnits.ether }
- Delta
-
-
-
+
+
+
+
+ Block
+ Txn
+ Age
+ Balance { currencyUnits.ether }
+ Delta
+
+
+
{ query.data.items.map((item, index) => (
{
isLoading={ query.isPlaceholderData }
/>
)) }
-
-
-
-
+
+
+
+
{ query.data.items.map((item, index) => (
{
isLoading={ query.isPlaceholderData }
/>
)) }
-
+
>
) : null;
@@ -71,11 +71,12 @@ const AddressCoinBalanceHistory = ({ query }: Props) => {
+ >
+ { content }
+
);
};
diff --git a/ui/address/coinBalance/AddressCoinBalanceListItem.tsx b/ui/address/coinBalance/AddressCoinBalanceListItem.tsx
index f138215be4..9d7b552a67 100644
--- a/ui/address/coinBalance/AddressCoinBalanceListItem.tsx
+++ b/ui/address/coinBalance/AddressCoinBalanceListItem.tsx
@@ -1,4 +1,4 @@
-import { Text, Stat, StatHelpText, StatArrow, Flex } from '@chakra-ui/react';
+import { Stat, Flex } from '@chakra-ui/react';
import BigNumber from 'bignumber.js';
import React from 'react';
@@ -6,7 +6,7 @@ import type { AddressCoinBalanceHistoryItem } from 'types/api/address';
import { WEI, ZERO } from 'lib/consts';
import { currencyUnits } from 'lib/units';
-import Skeleton from 'ui/shared/chakra/Skeleton';
+import { Skeleton } from 'toolkit/chakra/skeleton';
import BlockEntity from 'ui/shared/entities/block/BlockEntity';
import TxEntity from 'ui/shared/entities/tx/TxEntity';
import ListItemMobile from 'ui/shared/ListItemMobile/ListItemMobile';
@@ -22,24 +22,22 @@ const AddressCoinBalanceListItem = (props: Props) => {
const isPositiveDelta = deltaBn.gte(ZERO);
return (
-
+
-
+
{ BigNumber(props.value).div(WEI).dp(8).toFormat() } { currencyUnits.ether }
-
-
-
-
-
- { deltaBn.dp(8).toFormat() }
-
-
-
+
+
+
+ { deltaBn.dp(8).toFormat() }
+
+ { isPositiveDelta ? : }
+
- Block
+ Block
{
{ props.transaction_hash && (
- Txs
+ Txs
{
) }
- Age
+ Age
diff --git a/ui/address/coinBalance/AddressCoinBalanceTableItem.tsx b/ui/address/coinBalance/AddressCoinBalanceTableItem.tsx
index a12ea53b7a..17ddbd7551 100644
--- a/ui/address/coinBalance/AddressCoinBalanceTableItem.tsx
+++ b/ui/address/coinBalance/AddressCoinBalanceTableItem.tsx
@@ -1,11 +1,12 @@
-import { Td, Tr, Text, Stat, StatHelpText, StatArrow } from '@chakra-ui/react';
+import { Stat } from '@chakra-ui/react';
import BigNumber from 'bignumber.js';
import React from 'react';
import type { AddressCoinBalanceHistoryItem } from 'types/api/address';
import { WEI, ZERO } from 'lib/consts';
-import Skeleton from 'ui/shared/chakra/Skeleton';
+import { Skeleton } from 'toolkit/chakra/skeleton';
+import { TableCell, TableRow } from 'toolkit/chakra/table';
import BlockEntity from 'ui/shared/entities/block/BlockEntity';
import TxEntity from 'ui/shared/entities/tx/TxEntity';
import TimeAgoWithTooltip from 'ui/shared/TimeAgoWithTooltip';
@@ -20,18 +21,16 @@ const AddressCoinBalanceTableItem = (props: Props) => {
const isPositiveDelta = deltaBn.gte(ZERO);
return (
-
-
+
+
-
-
+
+
{ props.transaction_hash && (
{
maxW="150px"
/>
) }
-
-
+
+
-
-
-
+
+
+
{ BigNumber(props.value).div(WEI).dp(8).toFormat() }
-
-
-
-
-
-
-
- { deltaBn.dp(8).toFormat() }
-
-
-
+
+
+
+
+
+ { deltaBn.dp(8).toFormat() }
+
+ { isPositiveDelta ? : }
+
-
-
+
+
);
};
diff --git a/ui/address/contract/ContractCodeIdes.tsx b/ui/address/contract/ContractCodeIdes.tsx
index 0c8ca5cbf9..2d49b22cc5 100644
--- a/ui/address/contract/ContractCodeIdes.tsx
+++ b/ui/address/contract/ContractCodeIdes.tsx
@@ -1,30 +1,24 @@
-import {
- Flex,
- Button,
- chakra,
- PopoverTrigger,
- PopoverBody,
- PopoverContent,
- Image,
- useDisclosure,
- useColorModeValue,
-} from '@chakra-ui/react';
+import { Flex, chakra } from '@chakra-ui/react';
import React from 'react';
import config from 'configs/app';
-import Popover from 'ui/shared/chakra/Popover';
-import Skeleton from 'ui/shared/chakra/Skeleton';
+import { Button } from 'toolkit/chakra/button';
+import { useColorModeValue } from 'toolkit/chakra/color-mode';
+import { Image } from 'toolkit/chakra/image';
+import { Link } from 'toolkit/chakra/link';
+import { PopoverRoot, PopoverTrigger, PopoverContent, PopoverBody } from 'toolkit/chakra/popover';
+import { Skeleton } from 'toolkit/chakra/skeleton';
+import { useDisclosure } from 'toolkit/hooks/useDisclosure';
import IconSvg from 'ui/shared/IconSvg';
-import LinkExternal from 'ui/shared/links/LinkExternal';
interface Props {
className?: string;
hash: string;
- isLoading?: string;
+ isLoading?: boolean;
}
const ContractCodeIde = ({ className, hash, isLoading }: Props) => {
- const { isOpen, onToggle, onClose } = useDisclosure();
+ const { open, onOpenChange } = useDisclosure();
const defaultIconColor = useColorModeValue('gray.600', 'gray.500');
const ideLinks = React.useMemo(() => {
@@ -36,16 +30,16 @@ const ContractCodeIde = ({ className, hash, isLoading }: Props) => {
;
return (
-
+
{ icon }
{ ide.title }
-
+
);
});
}, [ defaultIconColor, hash ]);
if (isLoading) {
- return ;
+ return ;
}
if (ideLinks.length === 0) {
@@ -53,28 +47,25 @@ const ContractCodeIde = ({ className, hash, isLoading }: Props) => {
}
return (
-
+
Open in
-
+
- Redactors
+ Redactors
{
-
+
);
};
diff --git a/ui/address/contract/ContractDetails.pw.tsx b/ui/address/contract/ContractDetails.pw.tsx
index 1313f33ee3..b4ddb7fa3a 100644
--- a/ui/address/contract/ContractDetails.pw.tsx
+++ b/ui/address/contract/ContractDetails.pw.tsx
@@ -40,7 +40,8 @@ test.describe('full view', () => {
},
};
const component = await render( , { hooksConfig }, { withSocket: true });
- await createSocket();
+ const socket = await createSocket();
+ await socketServer.joinChannel(socket, `addresses:${ addressMock.contract.hash.toLowerCase() }`);
await expect(component).toHaveScreenshot();
});
@@ -51,7 +52,8 @@ test.describe('full view', () => {
},
};
const component = await render( , { hooksConfig }, { withSocket: true });
- await createSocket();
+ const socket = await createSocket();
+ await socketServer.joinChannel(socket, `addresses:${ addressMock.contract.hash.toLowerCase() }`);
await expect(component).toHaveScreenshot();
});
@@ -62,7 +64,8 @@ test.describe('full view', () => {
},
};
const component = await render( , { hooksConfig }, { withSocket: true });
- await createSocket();
+ const socket = await createSocket();
+ await socketServer.joinChannel(socket, `addresses:${ addressMock.contract.hash.toLowerCase() }`);
await expect(component).toHaveScreenshot();
});
@@ -73,7 +76,8 @@ test.describe('full view', () => {
},
};
const component = await render( , { hooksConfig }, { withSocket: true });
- await createSocket();
+ const socket = await createSocket();
+ await socketServer.joinChannel(socket, `addresses:${ addressMock.contract.hash.toLowerCase() }`);
await expect(component).toHaveScreenshot();
});
});
@@ -85,7 +89,8 @@ test.describe('mobile view', () => {
await mockApiResponse('contract', contractMock.withChangedByteCode, { pathParams: { hash: addressMock.contract.hash } });
await mockApiResponse('contract', contractMock.withChangedByteCode, { pathParams: { hash: addressMock.contract.implementations?.[0].address as string } });
const component = await render( , { hooksConfig }, { withSocket: true });
- await createSocket();
+ const socket = await createSocket();
+ await socketServer.joinChannel(socket, `addresses:${ addressMock.contract.hash.toLowerCase() }`);
await expect(component).toHaveScreenshot();
});
});
@@ -95,7 +100,7 @@ test('verified via lookup in eth_bytecode_db', async({ render, mockApiResponse,
await render( , { hooksConfig }, { withSocket: true });
const socket = await createSocket();
- const channel = await socketServer.joinChannel(socket, 'addresses:' + addressMock.contract.hash.toLowerCase());
+ const channel = await socketServer.joinChannel(socket, `addresses:${ addressMock.contract.hash.toLowerCase() }`);
await page.waitForResponse(contractApiUrl);
socketServer.sendMessage(socket, channel, 'smart_contract_was_verified', {});
const request = await page.waitForRequest(addressApiUrl);
@@ -103,9 +108,11 @@ test('verified via lookup in eth_bytecode_db', async({ render, mockApiResponse,
expect(request).toBeTruthy();
});
-test('verified with multiple sources', async({ render, page, mockApiResponse }) => {
+test('verified with multiple sources', async({ render, page, mockApiResponse, createSocket }) => {
await mockApiResponse('contract', contractMock.withMultiplePaths, { pathParams: { hash: addressMock.contract.hash } });
await render( , { hooksConfig }, { withSocket: true });
+ const socket = await createSocket();
+ await socketServer.joinChannel(socket, `addresses:${ addressMock.contract.hash.toLowerCase() }`);
const section = page.locator('section', { hasText: 'Contract source code' });
await expect(section).toHaveScreenshot();
@@ -117,7 +124,7 @@ test('verified with multiple sources', async({ render, page, mockApiResponse })
await expect(section).toHaveScreenshot();
});
-test('self destructed', async({ render, mockApiResponse, page }) => {
+test('self destructed', async({ render, mockApiResponse, page, createSocket }) => {
const hooksConfig = {
router: {
query: { hash: addressMock.contract.hash, tab: 'contract_bytecode' },
@@ -125,15 +132,19 @@ test('self destructed', async({ render, mockApiResponse, page }) => {
};
await mockApiResponse('contract', contractMock.selfDestructed, { pathParams: { hash: addressMock.contract.hash } });
await render( , { hooksConfig }, { withSocket: true });
+ const socket = await createSocket();
+ await socketServer.joinChannel(socket, `addresses:${ addressMock.contract.hash.toLowerCase() }`);
const section = page.locator('section', { hasText: 'Contract creation code' });
await expect(section).toHaveScreenshot();
});
-test('non verified', async({ render, mockApiResponse }) => {
+test('non verified', async({ render, mockApiResponse, createSocket }) => {
await mockApiResponse('address', { ...addressMock.contract, name: null }, { pathParams: { hash: addressMock.contract.hash } });
await mockApiResponse('contract', contractMock.nonVerified, { pathParams: { hash: addressMock.contract.hash } });
const component = await render( , { hooksConfig }, { withSocket: true });
+ const socket = await createSocket();
+ await socketServer.joinChannel(socket, `addresses:${ addressMock.contract.hash.toLowerCase() }`);
await expect(component).toHaveScreenshot();
});
diff --git a/ui/address/contract/ContractDetails.tsx b/ui/address/contract/ContractDetails.tsx
index 5a0b0147b4..8f369b3d44 100644
--- a/ui/address/contract/ContractDetails.tsx
+++ b/ui/address/contract/ContractDetails.tsx
@@ -15,8 +15,8 @@ import useApiQuery, { getResourceKey } from 'lib/api/useApiQuery';
import getQueryParamString from 'lib/router/getQueryParamString';
import useSocketMessage from 'lib/socket/useSocketMessage';
import * as stubs from 'stubs/contract';
+import RoutedTabs from 'toolkit/components/RoutedTabs/RoutedTabs';
import DataFetchAlert from 'ui/shared/DataFetchAlert';
-import RoutedTabs from 'ui/shared/Tabs/RoutedTabs';
import ContractDetailsAlerts from './alerts/ContractDetailsAlerts';
import ContractSourceAddressSelector from './ContractSourceAddressSelector';
@@ -57,7 +57,7 @@ const ContractDetails = ({ addressHash, channel, mainContractQuery }: Props) =>
const contractQuery = useApiQuery('contract', {
pathParams: { hash: selectedItem?.address },
queryOptions: {
- enabled: Boolean(selectedItem?.address),
+ enabled: Boolean(selectedItem?.address && !mainContractQuery.isPlaceholderData),
refetchOnMount: false,
placeholderData: addressInfo?.is_verified ? stubs.CONTRACT_CODE_VERIFIED : stubs.CONTRACT_CODE_UNVERIFIED,
},
@@ -115,10 +115,10 @@ const ContractDetails = ({ addressHash, channel, mainContractQuery }: Props) =>
) : (
diff --git a/ui/address/contract/ContractDetailsVerificationButton.tsx b/ui/address/contract/ContractDetailsVerificationButton.tsx
index 0f23d96f54..5b7e84e72c 100644
--- a/ui/address/contract/ContractDetailsVerificationButton.tsx
+++ b/ui/address/contract/ContractDetailsVerificationButton.tsx
@@ -1,9 +1,9 @@
-import { Button } from '@chakra-ui/react';
import React from 'react';
import { route } from 'nextjs-routes';
-import Skeleton from 'ui/shared/chakra/Skeleton';
+import { Button } from 'toolkit/chakra/button';
+import { Link } from 'toolkit/chakra/link';
interface Props {
isLoading: boolean;
@@ -12,29 +12,21 @@ interface Props {
}
const ContractDetailsVerificationButton = ({ isLoading, addressHash, isPartiallyVerified }: Props) => {
- if (isLoading) {
- return (
-
- );
- }
return (
-
- Verify & publish
-
+
+ Verify & publish
+
+
);
};
diff --git a/ui/address/contract/ContractExternalLibraries.tsx b/ui/address/contract/ContractExternalLibraries.tsx
index 08a940c540..f5ba405ade 100644
--- a/ui/address/contract/ContractExternalLibraries.tsx
+++ b/ui/address/contract/ContractExternalLibraries.tsx
@@ -1,27 +1,17 @@
-import {
- Alert,
- Box,
- Button,
- Flex,
- Heading,
- Modal,
- ModalCloseButton,
- ModalContent,
- PopoverBody,
- PopoverContent,
- PopoverTrigger,
- StackDivider,
- useDisclosure,
- VStack,
-} from '@chakra-ui/react';
+import { Box, Flex, Separator, VStack } from '@chakra-ui/react';
import React from 'react';
import type { SmartContractExternalLibrary } from 'types/api/contract';
import useIsMobile from 'lib/hooks/useIsMobile';
import { apos } from 'lib/html-entities';
-import Popover from 'ui/shared/chakra/Popover';
-import Skeleton from 'ui/shared/chakra/Skeleton';
+import { Alert } from 'toolkit/chakra/alert';
+import { Button } from 'toolkit/chakra/button';
+import { DialogBody, DialogContent, DialogHeader, DialogRoot } from 'toolkit/chakra/dialog';
+import { Heading } from 'toolkit/chakra/heading';
+import { PopoverRoot, PopoverBody, PopoverContent, PopoverTrigger } from 'toolkit/chakra/popover';
+import { Skeleton } from 'toolkit/chakra/skeleton';
+import { useDisclosure } from 'toolkit/hooks/useDisclosure';
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import IconSvg from 'ui/shared/IconSvg';
@@ -47,11 +37,11 @@ const Item = (data: SmartContractExternalLibrary) => {
};
const ContractExternalLibraries = ({ className, data, isLoading }: Props) => {
- const { isOpen, onToggle, onClose } = useDisclosure();
+ const { open, onToggle, onOpenChange } = useDisclosure();
const isMobile = useIsMobile();
if (isLoading) {
- return ;
+ return ;
}
if (data.length === 0) {
@@ -62,17 +52,17 @@ const ContractExternalLibraries = ({ className, data, isLoading }: Props) => {
{ data.length } { data.length > 1 ? 'Libraries' : 'Library' }
-
+
);
@@ -84,8 +74,8 @@ const ContractExternalLibraries = ({ className, data, isLoading }: Props) => {
Check the source code at the library address (if any) if you want to be sure in case if there is any library linked
}
- spacing={ 2 }
+ separator={ }
+ gap={ 2 }
mt={ 4 }
maxH={{ lg: '50vh' }}
overflowY="scroll"
@@ -99,18 +89,20 @@ const ContractExternalLibraries = ({ className, data, isLoading }: Props) => {
return (
<>
{ button }
-
-
-
- { content }
-
-
+
+
+
+
+ { content }
+
+
+
>
);
}
return (
-
+
{ button }
@@ -119,7 +111,7 @@ const ContractExternalLibraries = ({ className, data, isLoading }: Props) => {
{ content }
-
+
);
};
diff --git a/ui/address/contract/ContractSourceAddressSelector.tsx b/ui/address/contract/ContractSourceAddressSelector.tsx
index 3e92d3374a..25cf1f9bfa 100644
--- a/ui/address/contract/ContractSourceAddressSelector.tsx
+++ b/ui/address/contract/ContractSourceAddressSelector.tsx
@@ -1,13 +1,13 @@
-import { chakra, Flex } from '@chakra-ui/react';
+import { chakra, createListCollection, Flex } from '@chakra-ui/react';
import React from 'react';
import { route } from 'nextjs-routes';
-import Skeleton from 'ui/shared/chakra/Skeleton';
+import { Select } from 'toolkit/chakra/select';
+import { Skeleton } from 'toolkit/chakra/skeleton';
import CopyToClipboard from 'ui/shared/CopyToClipboard';
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import LinkNewTab from 'ui/shared/links/LinkNewTab';
-import Select from 'ui/shared/select/Select';
export interface Item {
address: string;
@@ -25,19 +25,20 @@ interface Props {
const ContractSourceAddressSelector = ({ className, selectedItem, onItemSelect, items, isLoading, label }: Props) => {
- const handleItemSelect = React.useCallback((value: string) => {
- const nextOption = items.find(({ address }) => address === value);
+ const handleItemSelect = React.useCallback(({ value }: { value: Array }) => {
+ const nextOption = items.find(({ address }) => address === value[0]);
if (nextOption) {
onItemSelect(nextOption);
}
}, [ items, onItemSelect ]);
- const options = React.useMemo(() => {
- return items.map(({ address, name }) => ({ label: name || address, value: address }));
+ const collection = React.useMemo(() => {
+ const options = items.map(({ address, name }) => ({ label: name || address, value: address }));
+ return createListCollection({ items: options });
}, [ items ]);
if (isLoading) {
- return ;
+ return ;
}
if (items.length === 0) {
@@ -59,15 +60,15 @@ const ContractSourceAddressSelector = ({ className, selectedItem, onItemSelect,
{ label }
-
+
}, [ data ]);
const heading = (
-
+
Contract source code
{ data?.language &&
- ({ formatLanguageName(data.language) }) }
+ ({ formatLanguageName(data.language) }) }
);
@@ -66,16 +67,16 @@ export const ContractSourceCode = ({ data, isLoading, sourceAddress }: Props) =>
null;
const diagramLink = data?.can_be_visualized_via_sol2uml ? (
-
-
+
-
+
View UML diagram
-
+
) : null;
@@ -83,7 +84,7 @@ export const ContractSourceCode = ({ data, isLoading, sourceAddress }: Props) =>
:
null;
- const copyToClipboard = data && editorData?.length === 1 ? (
+ const copyToClipboard = data && editorData?.length === 1 && data.source_code ? (
const content = (() => {
if (isLoading) {
- return ;
+ return ;
}
if (!editorData) {
diff --git a/ui/address/contract/__screenshots__/ContractDetails.pw.tsx_dark-color-mode_full-view-source-code-dark-mode-1.png b/ui/address/contract/__screenshots__/ContractDetails.pw.tsx_dark-color-mode_full-view-source-code-dark-mode-1.png
index 65bc859a78..9f1854998c 100644
Binary files a/ui/address/contract/__screenshots__/ContractDetails.pw.tsx_dark-color-mode_full-view-source-code-dark-mode-1.png and b/ui/address/contract/__screenshots__/ContractDetails.pw.tsx_dark-color-mode_full-view-source-code-dark-mode-1.png differ
diff --git a/ui/address/contract/__screenshots__/ContractDetails.pw.tsx_default_full-view-abi-1.png b/ui/address/contract/__screenshots__/ContractDetails.pw.tsx_default_full-view-abi-1.png
index 40ebfae091..ed380abd71 100644
Binary files a/ui/address/contract/__screenshots__/ContractDetails.pw.tsx_default_full-view-abi-1.png and b/ui/address/contract/__screenshots__/ContractDetails.pw.tsx_default_full-view-abi-1.png differ
diff --git a/ui/address/contract/__screenshots__/ContractDetails.pw.tsx_default_full-view-bytecode-1.png b/ui/address/contract/__screenshots__/ContractDetails.pw.tsx_default_full-view-bytecode-1.png
index e82936fdc8..28706e9fc4 100644
Binary files a/ui/address/contract/__screenshots__/ContractDetails.pw.tsx_default_full-view-bytecode-1.png and b/ui/address/contract/__screenshots__/ContractDetails.pw.tsx_default_full-view-bytecode-1.png differ
diff --git a/ui/address/contract/__screenshots__/ContractDetails.pw.tsx_default_full-view-compiler-1.png b/ui/address/contract/__screenshots__/ContractDetails.pw.tsx_default_full-view-compiler-1.png
index f2b4f8c920..65bda9bd55 100644
Binary files a/ui/address/contract/__screenshots__/ContractDetails.pw.tsx_default_full-view-compiler-1.png and b/ui/address/contract/__screenshots__/ContractDetails.pw.tsx_default_full-view-compiler-1.png differ
diff --git a/ui/address/contract/__screenshots__/ContractDetails.pw.tsx_default_full-view-source-code-dark-mode-1.png b/ui/address/contract/__screenshots__/ContractDetails.pw.tsx_default_full-view-source-code-dark-mode-1.png
index ea3251f534..f0175aca0f 100644
Binary files a/ui/address/contract/__screenshots__/ContractDetails.pw.tsx_default_full-view-source-code-dark-mode-1.png and b/ui/address/contract/__screenshots__/ContractDetails.pw.tsx_default_full-view-source-code-dark-mode-1.png differ
diff --git a/ui/address/contract/__screenshots__/ContractDetails.pw.tsx_default_mobile-view-source-code-1.png b/ui/address/contract/__screenshots__/ContractDetails.pw.tsx_default_mobile-view-source-code-1.png
index a0d74ca0b3..70ad557b09 100644
Binary files a/ui/address/contract/__screenshots__/ContractDetails.pw.tsx_default_mobile-view-source-code-1.png and b/ui/address/contract/__screenshots__/ContractDetails.pw.tsx_default_mobile-view-source-code-1.png differ
diff --git a/ui/address/contract/__screenshots__/ContractDetails.pw.tsx_default_non-verified-1.png b/ui/address/contract/__screenshots__/ContractDetails.pw.tsx_default_non-verified-1.png
index 7d4a2f52bd..051eb61e7c 100644
Binary files a/ui/address/contract/__screenshots__/ContractDetails.pw.tsx_default_non-verified-1.png and b/ui/address/contract/__screenshots__/ContractDetails.pw.tsx_default_non-verified-1.png differ
diff --git a/ui/address/contract/__screenshots__/ContractDetails.pw.tsx_default_self-destructed-1.png b/ui/address/contract/__screenshots__/ContractDetails.pw.tsx_default_self-destructed-1.png
index d13a9f2164..c15b922be6 100644
Binary files a/ui/address/contract/__screenshots__/ContractDetails.pw.tsx_default_self-destructed-1.png and b/ui/address/contract/__screenshots__/ContractDetails.pw.tsx_default_self-destructed-1.png differ
diff --git a/ui/address/contract/__screenshots__/ContractDetails.pw.tsx_default_verified-with-multiple-sources-1.png b/ui/address/contract/__screenshots__/ContractDetails.pw.tsx_default_verified-with-multiple-sources-1.png
index f6759f3cbb..b132bae58d 100644
Binary files a/ui/address/contract/__screenshots__/ContractDetails.pw.tsx_default_verified-with-multiple-sources-1.png and b/ui/address/contract/__screenshots__/ContractDetails.pw.tsx_default_verified-with-multiple-sources-1.png differ
diff --git a/ui/address/contract/__screenshots__/ContractDetails.pw.tsx_default_verified-with-multiple-sources-2.png b/ui/address/contract/__screenshots__/ContractDetails.pw.tsx_default_verified-with-multiple-sources-2.png
index 76c0952e84..28408db572 100644
Binary files a/ui/address/contract/__screenshots__/ContractDetails.pw.tsx_default_verified-with-multiple-sources-2.png and b/ui/address/contract/__screenshots__/ContractDetails.pw.tsx_default_verified-with-multiple-sources-2.png differ
diff --git a/ui/address/contract/__screenshots__/ContractDetails.pw.tsx_default_verified-with-multiple-sources-3.png b/ui/address/contract/__screenshots__/ContractDetails.pw.tsx_default_verified-with-multiple-sources-3.png
index 9b53a7ebc4..e83cd131e0 100644
Binary files a/ui/address/contract/__screenshots__/ContractDetails.pw.tsx_default_verified-with-multiple-sources-3.png and b/ui/address/contract/__screenshots__/ContractDetails.pw.tsx_default_verified-with-multiple-sources-3.png differ
diff --git a/ui/address/contract/alerts/ContractDetailsAlertProxyPattern.tsx b/ui/address/contract/alerts/ContractDetailsAlertProxyPattern.tsx
index 46d24bb3cd..2ed673a569 100644
--- a/ui/address/contract/alerts/ContractDetailsAlertProxyPattern.tsx
+++ b/ui/address/contract/alerts/ContractDetailsAlertProxyPattern.tsx
@@ -1,9 +1,9 @@
-import { Alert } from '@chakra-ui/react';
import React from 'react';
import type { SmartContractProxyType } from 'types/api/contract';
-import LinkExternal from 'ui/shared/links/LinkExternal';
+import { Alert } from 'toolkit/chakra/alert';
+import { Link } from 'toolkit/chakra/link';
interface Props {
type: NonNullable;
@@ -70,10 +70,10 @@ const ContractCodeProxyPattern = ({ type }: Props) => {
}
return (
-
+
{ proxyInfo.link ? (
<>
- This proxy smart-contract is detected via { proxyInfo.name }
+ This proxy smart-contract is detected via { proxyInfo.name }
{ proxyInfo.description && ` - ${ proxyInfo.description }` }
>
) : (
diff --git a/ui/address/contract/alerts/ContractDetailsAlertVerificationSource.tsx b/ui/address/contract/alerts/ContractDetailsAlertVerificationSource.tsx
index 27062c54f4..2e08447b2a 100644
--- a/ui/address/contract/alerts/ContractDetailsAlertVerificationSource.tsx
+++ b/ui/address/contract/alerts/ContractDetailsAlertVerificationSource.tsx
@@ -1,9 +1,9 @@
-import { Alert } from '@chakra-ui/react';
import React from 'react';
import type { SmartContract } from 'types/api/contract';
-import LinkExternal from 'ui/shared/links/LinkExternal';
+import { Alert } from 'toolkit/chakra/alert';
+import { Link } from 'toolkit/chakra/link';
interface Props {
data: SmartContract | undefined;
@@ -12,23 +12,24 @@ interface Props {
const ContractDetailsAlertVerificationSource = ({ data }: Props) => {
if (data?.is_verified && data?.is_verified_via_eth_bytecode_db) {
return (
-
+
This contract has been { data.is_partially_verified ? 'partially ' : '' }verified using
-
Blockscout Bytecode Database
-
+
);
}
if (data?.is_verified && data?.is_verified_via_sourcify) {
return (
-
+
This contract has been { data.is_partially_verified ? 'partially ' : '' }verified via Sourcify.
- { data.sourcify_repo_url && View contract in Sourcify repository }
+ { data.sourcify_repo_url && View contract in Sourcify repository }
);
}
diff --git a/ui/address/contract/alerts/ContractDetailsAlerts.tsx b/ui/address/contract/alerts/ContractDetailsAlerts.tsx
index 4bf02c15a6..0fdcf918f4 100644
--- a/ui/address/contract/alerts/ContractDetailsAlerts.tsx
+++ b/ui/address/contract/alerts/ContractDetailsAlerts.tsx
@@ -1,4 +1,4 @@
-import { chakra, Alert, Box, Flex } from '@chakra-ui/react';
+import { chakra, Box, Flex } from '@chakra-ui/react';
import type { Channel } from 'phoenix';
import React from 'react';
@@ -8,10 +8,10 @@ import type { SmartContract } from 'types/api/contract';
import { route } from 'nextjs-routes';
import useSocketMessage from 'lib/socket/useSocketMessage';
-import Skeleton from 'ui/shared/chakra/Skeleton';
+import { Alert } from 'toolkit/chakra/alert';
+import { Link } from 'toolkit/chakra/link';
+import { Skeleton } from 'toolkit/chakra/skeleton';
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
-import LinkExternal from 'ui/shared/links/LinkExternal';
-import LinkInternal from 'ui/shared/links/LinkInternal';
import ContractDetailsVerificationButton from '../ContractDetailsVerificationButton';
import ContractDetailsAlertProxyPattern from './ContractDetailsAlertProxyPattern';
@@ -42,14 +42,14 @@ const ContractDetailsAlerts = ({ data, isLoading, addressHash, channel }: Props)
{ data?.is_blueprint && (
This is an
-
+
ERC-5202 Blueprint contract
-
+
) }
{ data?.is_verified && (
-
-
+
+
Contract Source Code Verified ({ data.is_partially_verified ? 'Partial' : 'Exact' } Match)
{
data.is_partially_verified ? (
@@ -70,7 +70,7 @@ const ContractDetailsAlerts = ({ data, isLoading, addressHash, channel }: Props)
) }
{ !data?.is_verified && data?.verified_twin_address_hash && (!data?.proxy_type || data.proxy_type === 'unknown') && (
-
+
Contract is not verified. However, we found a verified contract with the same bytecode in Blockscout DB
All functions displayed below are from ABI of that contract. In order to verify current contract, proceed with
-
+
Verify & Publish
-
+
page
) }
diff --git a/ui/address/contract/alerts/__screenshots__/ContractDetailsAlertProxyPattern.pw.tsx_default_proxy-type-with-link-but-without-description-1.png b/ui/address/contract/alerts/__screenshots__/ContractDetailsAlertProxyPattern.pw.tsx_default_proxy-type-with-link-but-without-description-1.png
index 4358b0c66d..b99641b303 100644
Binary files a/ui/address/contract/alerts/__screenshots__/ContractDetailsAlertProxyPattern.pw.tsx_default_proxy-type-with-link-but-without-description-1.png and b/ui/address/contract/alerts/__screenshots__/ContractDetailsAlertProxyPattern.pw.tsx_default_proxy-type-with-link-but-without-description-1.png differ
diff --git a/ui/address/contract/alerts/__screenshots__/ContractDetailsAlertProxyPattern.pw.tsx_default_proxy-type-with-link-mobile-1.png b/ui/address/contract/alerts/__screenshots__/ContractDetailsAlertProxyPattern.pw.tsx_default_proxy-type-with-link-mobile-1.png
index 3dc888ede0..f7bf9d08f8 100644
Binary files a/ui/address/contract/alerts/__screenshots__/ContractDetailsAlertProxyPattern.pw.tsx_default_proxy-type-with-link-mobile-1.png and b/ui/address/contract/alerts/__screenshots__/ContractDetailsAlertProxyPattern.pw.tsx_default_proxy-type-with-link-mobile-1.png differ
diff --git a/ui/address/contract/alerts/__screenshots__/ContractDetailsAlertProxyPattern.pw.tsx_default_proxy-type-without-link-1.png b/ui/address/contract/alerts/__screenshots__/ContractDetailsAlertProxyPattern.pw.tsx_default_proxy-type-without-link-1.png
index a277fc8260..fcc2831049 100644
Binary files a/ui/address/contract/alerts/__screenshots__/ContractDetailsAlertProxyPattern.pw.tsx_default_proxy-type-without-link-1.png and b/ui/address/contract/alerts/__screenshots__/ContractDetailsAlertProxyPattern.pw.tsx_default_proxy-type-without-link-1.png differ
diff --git a/ui/address/contract/alerts/__screenshots__/ContractDetailsAlertProxyPattern.pw.tsx_mobile_proxy-type-with-link-mobile-1.png b/ui/address/contract/alerts/__screenshots__/ContractDetailsAlertProxyPattern.pw.tsx_mobile_proxy-type-with-link-mobile-1.png
index 3809c509a9..e550f66c37 100644
Binary files a/ui/address/contract/alerts/__screenshots__/ContractDetailsAlertProxyPattern.pw.tsx_mobile_proxy-type-with-link-mobile-1.png and b/ui/address/contract/alerts/__screenshots__/ContractDetailsAlertProxyPattern.pw.tsx_mobile_proxy-type-with-link-mobile-1.png differ
diff --git a/ui/address/contract/alerts/__screenshots__/ContractDetailsAlerts.pw.tsx_default_verified-via-eth-bytecode-db-1.png b/ui/address/contract/alerts/__screenshots__/ContractDetailsAlerts.pw.tsx_default_verified-via-eth-bytecode-db-1.png
index 0eae8612fd..d3ace1ba79 100644
Binary files a/ui/address/contract/alerts/__screenshots__/ContractDetailsAlerts.pw.tsx_default_verified-via-eth-bytecode-db-1.png and b/ui/address/contract/alerts/__screenshots__/ContractDetailsAlerts.pw.tsx_default_verified-via-eth-bytecode-db-1.png differ
diff --git a/ui/address/contract/alerts/__screenshots__/ContractDetailsAlerts.pw.tsx_default_verified-via-sourcify-1.png b/ui/address/contract/alerts/__screenshots__/ContractDetailsAlerts.pw.tsx_default_verified-via-sourcify-1.png
index a0542c3647..1ece8db35e 100644
Binary files a/ui/address/contract/alerts/__screenshots__/ContractDetailsAlerts.pw.tsx_default_verified-via-sourcify-1.png and b/ui/address/contract/alerts/__screenshots__/ContractDetailsAlerts.pw.tsx_default_verified-via-sourcify-1.png differ
diff --git a/ui/address/contract/alerts/__screenshots__/ContractDetailsAlerts.pw.tsx_default_verified-with-changed-byte-code-socket-1.png b/ui/address/contract/alerts/__screenshots__/ContractDetailsAlerts.pw.tsx_default_verified-with-changed-byte-code-socket-1.png
index 4758938b38..d6f8f0c0d6 100644
Binary files a/ui/address/contract/alerts/__screenshots__/ContractDetailsAlerts.pw.tsx_default_verified-with-changed-byte-code-socket-1.png and b/ui/address/contract/alerts/__screenshots__/ContractDetailsAlerts.pw.tsx_default_verified-with-changed-byte-code-socket-1.png differ
diff --git a/ui/address/contract/alerts/__screenshots__/ContractDetailsAlerts.pw.tsx_default_with-twin-address-alert-mobile-1.png b/ui/address/contract/alerts/__screenshots__/ContractDetailsAlerts.pw.tsx_default_with-twin-address-alert-mobile-1.png
index 019443f452..baaa0bf935 100644
Binary files a/ui/address/contract/alerts/__screenshots__/ContractDetailsAlerts.pw.tsx_default_with-twin-address-alert-mobile-1.png and b/ui/address/contract/alerts/__screenshots__/ContractDetailsAlerts.pw.tsx_default_with-twin-address-alert-mobile-1.png differ
diff --git a/ui/address/contract/alerts/__screenshots__/ContractDetailsAlerts.pw.tsx_mobile_with-twin-address-alert-mobile-1.png b/ui/address/contract/alerts/__screenshots__/ContractDetailsAlerts.pw.tsx_mobile_with-twin-address-alert-mobile-1.png
index 01cc90e853..19fbd08cbe 100644
Binary files a/ui/address/contract/alerts/__screenshots__/ContractDetailsAlerts.pw.tsx_mobile_with-twin-address-alert-mobile-1.png and b/ui/address/contract/alerts/__screenshots__/ContractDetailsAlerts.pw.tsx_mobile_with-twin-address-alert-mobile-1.png differ
diff --git a/ui/address/contract/audits/ContractSecurityAudits.tsx b/ui/address/contract/audits/ContractSecurityAudits.tsx
index 970fac1706..b04caa1c16 100644
--- a/ui/address/contract/audits/ContractSecurityAudits.tsx
+++ b/ui/address/contract/audits/ContractSecurityAudits.tsx
@@ -1,13 +1,15 @@
-import { Box, Button, useDisclosure } from '@chakra-ui/react';
+import { Box } from '@chakra-ui/react';
import React from 'react';
import type { SmartContractSecurityAuditSubmission } from 'types/api/contract';
import useApiQuery from 'lib/api/useApiQuery';
import dayjs from 'lib/date/dayjs';
+import { Button } from 'toolkit/chakra/button';
+import { Link } from 'toolkit/chakra/link';
+import { useDisclosure } from 'toolkit/hooks/useDisclosure';
import ContainerWithScrollY from 'ui/shared/ContainerWithScrollY';
import FormModal from 'ui/shared/FormModal';
-import LinkExternal from 'ui/shared/links/LinkExternal';
import ContractSubmitAuditForm from './ContractSubmitAuditForm';
@@ -46,16 +48,16 @@ const ContractSecurityAudits = ({ addressHash }: Props) => {
mt={ 2 }
>
{ data.items.map(item => (
-
+
{ `${ item.audit_company_name }, ${ dayjs(item.audit_publish_date).format('MMM DD, YYYY') }` }
-
+
)) }
) }
- isOpen={ modalProps.isOpen }
- onClose={ modalProps.onClose }
+ open={ modalProps.open }
+ onOpenChange={ modalProps.onOpenChange }
title={ formTitle }
renderForm={ renderForm }
/>
diff --git a/ui/address/contract/audits/ContractSubmitAuditForm.tsx b/ui/address/contract/audits/ContractSubmitAuditForm.tsx
index 1fdc735531..1a4f8e7885 100644
--- a/ui/address/contract/audits/ContractSubmitAuditForm.tsx
+++ b/ui/address/contract/audits/ContractSubmitAuditForm.tsx
@@ -1,4 +1,4 @@
-import { Button, VStack } from '@chakra-ui/react';
+import { VStack } from '@chakra-ui/react';
import React from 'react';
import type { SubmitHandler } from 'react-hook-form';
import { FormProvider, useForm } from 'react-hook-form';
@@ -8,7 +8,8 @@ import type { SmartContractSecurityAuditSubmission } from 'types/api/contract';
import type { ResourceError } from 'lib/api/resources';
import useApiFetch from 'lib/api/useApiFetch';
import dayjs from 'lib/date/dayjs';
-import useToast from 'lib/hooks/useToast';
+import { Button } from 'toolkit/chakra/button';
+import { toaster } from 'toolkit/chakra/toaster';
import FormFieldCheckbox from 'ui/shared/forms/fields/FormFieldCheckbox';
import FormFieldEmail from 'ui/shared/forms/fields/FormFieldEmail';
import FormFieldText from 'ui/shared/forms/fields/FormFieldText';
@@ -39,7 +40,6 @@ const ContractSubmitAuditForm = ({ address, onSuccess }: Props) => {
const containerRef = React.useRef(null);
const apiFetch = useApiFetch();
- const toast = useToast();
const formApi = useForm({
mode: 'onTouched',
@@ -57,13 +57,9 @@ const ContractSubmitAuditForm = ({ address, onSuccess }: Props) => {
},
});
- toast({
- position: 'top-right',
+ toaster.success({
title: 'Success',
description: 'Your audit report has been successfully submitted for review',
- status: 'success',
- variant: 'subtle',
- isClosable: true,
});
onSuccess();
@@ -77,37 +73,32 @@ const ContractSubmitAuditForm = ({ address, onSuccess }: Props) => {
setError(errorField, { type: 'custom', message: errorMap[errorField].join(', ') });
});
} else {
- toast({
- position: 'top-right',
+ toaster.error({
title: 'Error',
description: (_error as ResourceError<{ message: string }>)?.payload?.message || 'Something went wrong. Try again later.',
- status: 'error',
- variant: 'subtle',
- isClosable: true,
});
}
}
- }, [ apiFetch, address, toast, setError, onSuccess ]);
+ }, [ apiFetch, address, setError, onSuccess ]);
return (