Commit 9f6bd932 authored by Max Alekseenko's avatar Max Alekseenko

Merge branch 'main' into defi-dropdown

parents 7748b120 6cdaafc0
...@@ -11,6 +11,16 @@ on: ...@@ -11,6 +11,16 @@ on:
description: JSON encoded list of issue ids description: JSON encoded list of issue ids
required: true required: true
type: string type: string
workflow_call:
inputs:
pr_number:
description: Pull request number
required: true
type: string
issues:
description: JSON encoded list of issue ids
required: true
type: string
jobs: jobs:
run: run:
......
...@@ -12,8 +12,8 @@ ...@@ -12,8 +12,8 @@
# next.js # next.js
/.next/ /.next/
/out/ /out/
/public/assets/ /public/assets/envs.js
/public/envs.js /public/assets/configs
/public/icons/sprite.svg /public/icons/sprite.svg
/public/icons/README.md /public/icons/README.md
/analyze /analyze
......
...@@ -34,11 +34,13 @@ RUN yarn --frozen-lockfile ...@@ -34,11 +34,13 @@ RUN yarn --frozen-lockfile
FROM node:20.11.0-alpine AS builder FROM node:20.11.0-alpine AS builder
RUN apk add --no-cache --upgrade libc6-compat bash RUN apk add --no-cache --upgrade libc6-compat bash
# pass commit sha and git tag to the app image # pass build args to env variables
ARG GIT_COMMIT_SHA ARG GIT_COMMIT_SHA
ENV NEXT_PUBLIC_GIT_COMMIT_SHA=$GIT_COMMIT_SHA ENV NEXT_PUBLIC_GIT_COMMIT_SHA=$GIT_COMMIT_SHA
ARG GIT_TAG ARG GIT_TAG
ENV NEXT_PUBLIC_GIT_TAG=$GIT_TAG ENV NEXT_PUBLIC_GIT_TAG=$GIT_TAG
ARG NEXT_OPEN_TELEMETRY_ENABLED
ENV NEXT_OPEN_TELEMETRY_ENABLED=$NEXT_OPEN_TELEMETRY_ENABLED
ENV NODE_ENV production ENV NODE_ENV production
...@@ -58,8 +60,8 @@ RUN ./collect_envs.sh ./docs/ENVS.md ...@@ -58,8 +60,8 @@ RUN ./collect_envs.sh ./docs/ENVS.md
# ENV NEXT_TELEMETRY_DISABLED 1 # ENV NEXT_TELEMETRY_DISABLED 1
# Build app for production # Build app for production
RUN yarn build
RUN yarn svg:build-sprite RUN yarn svg:build-sprite
RUN yarn build
### FEATURE REPORTER ### FEATURE REPORTER
......
...@@ -41,7 +41,7 @@ export const buildExternalAssetFilePath = (name: string, value: string) => { ...@@ -41,7 +41,7 @@ export const buildExternalAssetFilePath = (name: string, value: string) => {
const fileName = name.replace(/^NEXT_PUBLIC_/, '').replace(/_URL$/, '').toLowerCase(); const fileName = name.replace(/^NEXT_PUBLIC_/, '').replace(/_URL$/, '').toLowerCase();
const url = new URL(value); const url = new URL(value);
const fileExtension = url.pathname.match(regexp.FILE_EXTENSION)?.[1]; const fileExtension = url.pathname.match(regexp.FILE_EXTENSION)?.[1];
return `/assets/${ fileName }.${ fileExtension }`; return `/assets/configs/${ fileName }.${ fileExtension }`;
} catch (error) { } catch (error) {
return; return;
} }
......
...@@ -24,8 +24,8 @@ NEXT_PUBLIC_API_BASE_PATH=/ ...@@ -24,8 +24,8 @@ NEXT_PUBLIC_API_BASE_PATH=/
# ui config # ui config
## homepage ## homepage
NEXT_PUBLIC_HOMEPAGE_CHARTS=['daily_txs'] NEXT_PUBLIC_HOMEPAGE_CHARTS=['daily_txs']
NEXT_PUBLIC_HOMEPAGE_PLATE_BACKGROUND='rgba(51, 53, 67, 1)' NEXT_PUBLIC_HOMEPAGE_PLATE_BACKGROUND=rgba(51,53,67,1)
NEXT_PUBLIC_HOMEPAGE_PLATE_TEXT_COLOR='rgba(165, 252, 122, 1)' NEXT_PUBLIC_HOMEPAGE_PLATE_TEXT_COLOR=rgba(165,252,122,1)
## sidebar ## sidebar
NEXT_PUBLIC_FEATURED_NETWORKS=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/featured-networks/eth-sepolia.json NEXT_PUBLIC_FEATURED_NETWORKS=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/featured-networks/eth-sepolia.json
NEXT_PUBLIC_NETWORK_LOGO=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-logos/sepolia.svg NEXT_PUBLIC_NETWORK_LOGO=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-logos/sepolia.svg
...@@ -47,7 +47,11 @@ NEXT_PUBLIC_IS_ACCOUNT_SUPPORTED=true ...@@ -47,7 +47,11 @@ NEXT_PUBLIC_IS_ACCOUNT_SUPPORTED=true
NEXT_PUBLIC_AUTH_URL=http://localhost:3000/login NEXT_PUBLIC_AUTH_URL=http://localhost:3000/login
NEXT_PUBLIC_LOGOUT_URL=https://blockscout-goerli.us.auth0.com/v2/logout NEXT_PUBLIC_LOGOUT_URL=https://blockscout-goerli.us.auth0.com/v2/logout
NEXT_PUBLIC_MARKETPLACE_CONFIG_URL=https://raw.githubusercontent.com/blockscout/frontend-configs/dev/configs/marketplace/eth-goerli.json NEXT_PUBLIC_MARKETPLACE_CONFIG_URL=https://raw.githubusercontent.com/blockscout/frontend-configs/dev/configs/marketplace/eth-goerli.json
NEXT_PUBLIC_MARKETPLACE_SUBMIT_FORM=https://airtable.com/shrqUAcjgGJ4jU88C NEXT_PUBLIC_MARKETPLACE_SECURITY_REPORTS_URL=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/marketplace-security-reports/default.json
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_SUGGEST_IDEAS_FORM=https://airtable.com/appiy5yijZpMMSKjT/pag3t82DUCyhGRZZO/form
NEXT_PUBLIC_MARKETPLACE_SUBMIT_FORM=https://airtable.com/appiy5yijZpMMSKjT/shr6uMGPKjj1DK7NL
NEXT_PUBLIC_STATS_API_HOST=https://stats-sepolia.k8s-dev.blockscout.com NEXT_PUBLIC_STATS_API_HOST=https://stats-sepolia.k8s-dev.blockscout.com
NEXT_PUBLIC_VISUALIZE_API_HOST=https://visualizer.services.blockscout.com NEXT_PUBLIC_VISUALIZE_API_HOST=https://visualizer.services.blockscout.com
NEXT_PUBLIC_CONTRACT_INFO_API_HOST=https://contracts-info.services.blockscout.com NEXT_PUBLIC_CONTRACT_INFO_API_HOST=https://contracts-info.services.blockscout.com
......
...@@ -36,7 +36,7 @@ export_envs_from_preset() { ...@@ -36,7 +36,7 @@ export_envs_from_preset() {
export_envs_from_preset export_envs_from_preset
# Download external assets # Download external assets
./download_assets.sh ./public/assets ./download_assets.sh ./public/assets/configs
# Check run-time ENVs values # Check run-time ENVs values
./validate_envs.sh ./validate_envs.sh
......
...@@ -10,7 +10,7 @@ if [ $? -ne 0 ]; then ...@@ -10,7 +10,7 @@ if [ $? -ne 0 ]; then
exit 1 exit 1
else else
cd ../../../ cd ../../../
favicon_folder="./public/favicon/" favicon_folder="./public/assets/favicon/"
echo "⏳ Replacing default favicons with freshly generated pack..." echo "⏳ Replacing default favicons with freshly generated pack..."
if [ -d "$favicon_folder" ]; then if [ -d "$favicon_folder" ]; then
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
echo "🌀 Creating client script with ENV values..." echo "🌀 Creating client script with ENV values..."
# Define the output file name # Define the output file name
output_file="${1:-./public/envs.js}" output_file="${1:-./public/assets/envs.js}"
touch $output_file; touch $output_file;
truncate -s 0 $output_file; truncate -s 0 $output_file;
......
...@@ -9,7 +9,7 @@ export NEXT_PUBLIC_GIT_TAG=$(git describe --tags --abbrev=0) ...@@ -9,7 +9,7 @@ export NEXT_PUBLIC_GIT_TAG=$(git describe --tags --abbrev=0)
../../scripts/collect_envs.sh ../../../docs/ENVS.md ../../scripts/collect_envs.sh ../../../docs/ENVS.md
# Copy test assets # Copy test assets
mkdir -p "./public/assets" mkdir -p "./public/assets/configs"
cp -r ${test_folder}/assets ./public/ cp -r ${test_folder}/assets ./public/
# Build validator script # Build validator script
......
...@@ -93,7 +93,6 @@ frontend: ...@@ -93,7 +93,6 @@ frontend:
NEXT_PUBLIC_AD_ADBUTLER_CONFIG_MOBILE: "{ \"id\": \"632018\", \"width\": \"320\", \"height\": \"100\" }" NEXT_PUBLIC_AD_ADBUTLER_CONFIG_MOBILE: "{ \"id\": \"632018\", \"width\": \"320\", \"height\": \"100\" }"
NEXT_PUBLIC_DATA_AVAILABILITY_ENABLED: true NEXT_PUBLIC_DATA_AVAILABILITY_ENABLED: true
NEXT_PUBLIC_OG_ENHANCED_DATA_ENABLED: true NEXT_PUBLIC_OG_ENHANCED_DATA_ENABLED: true
NEXT_PUBLIC_COLOR_THEME_DEFAULT: "dim"
envFromSecret: envFromSecret:
NEXT_PUBLIC_SENTRY_DSN: ref+vault://deployment-values/blockscout/dev/review?token_env=VAULT_TOKEN&address=https://vault.k8s.blockscout.com#/NEXT_PUBLIC_SENTRY_DSN NEXT_PUBLIC_SENTRY_DSN: ref+vault://deployment-values/blockscout/dev/review?token_env=VAULT_TOKEN&address=https://vault.k8s.blockscout.com#/NEXT_PUBLIC_SENTRY_DSN
SENTRY_CSP_REPORT_URI: ref+vault://deployment-values/blockscout/dev/review?token_env=VAULT_TOKEN&address=https://vault.k8s.blockscout.com#/SENTRY_CSP_REPORT_URI SENTRY_CSP_REPORT_URI: ref+vault://deployment-values/blockscout/dev/review?token_env=VAULT_TOKEN&address=https://vault.k8s.blockscout.com#/SENTRY_CSP_REPORT_URI
......
...@@ -661,11 +661,11 @@ The feature enables the Validators page which provides detailed information abou ...@@ -661,11 +661,11 @@ The feature enables the Validators page which provides detailed information abou
### OpenTelemetry ### OpenTelemetry
OpenTelemetry SDK for Node.js app could be enabled by passing `OTEL_SDK_ENABLED=true` variable. Configure the OpenTelemetry Protocol Exporter by using the generic environment variables described in the [OT docs](https://opentelemetry.io/docs/specs/otel/protocol/exporter/#configuration-options). OpenTelemetry SDK for Node.js app could be enabled by passing `OTEL_SDK_ENABLED=true` variable. Configure the OpenTelemetry Protocol Exporter by using the generic environment variables described in the [OT docs](https://opentelemetry.io/docs/specs/otel/protocol/exporter/#configuration-options). Note that this Next.js feature is currently experimental. The Docker image should be built with the `NEXT_OPEN_TELEMETRY_ENABLED=true` argument to enable it.
| Variable | Type| Description | Compulsoriness | Default value | Example value | | Variable | Type| Description | Compulsoriness | Default value | Example value |
| --- | --- | --- | --- | --- | --- | | --- | --- | --- | --- | --- | --- |
| OTEL_SDK_ENABLED | `boolean` | Flag to enable the feature | Required | `false` | `true` | | OTEL_SDK_ENABLED | `boolean` | Run-time flag to enable the feature | Required | `false` | `true` |
   
......
...@@ -46,9 +46,7 @@ const sdk = new NodeSDK({ ...@@ -46,9 +46,7 @@ const sdk = new NodeSDK({
url.pathname.startsWith('/_next/static/') || url.pathname.startsWith('/_next/static/') ||
url.pathname.startsWith('/_next/data/') || url.pathname.startsWith('/_next/data/') ||
url.pathname.startsWith('/assets/') || url.pathname.startsWith('/assets/') ||
url.pathname.startsWith('/static/') || url.pathname.startsWith('/static/')
url.pathname.startsWith('/favicon/') ||
url.pathname.startsWith('/envs.js')
) { ) {
return true; return true;
} }
......
...@@ -46,7 +46,7 @@ const moduleExports = { ...@@ -46,7 +46,7 @@ const moduleExports = {
output: 'standalone', output: 'standalone',
productionBrowserSourceMaps: true, productionBrowserSourceMaps: true,
experimental: { experimental: {
instrumentationHook: true, instrumentationHook: process.env.NEXT_OPEN_TELEMETRY_ENABLED === 'true',
turbo: { turbo: {
rules: { rules: {
'*.svg': { '*.svg': {
......
...@@ -56,6 +56,7 @@ export function app(): CspDev.DirectiveDescriptor { ...@@ -56,6 +56,7 @@ export function app(): CspDev.DirectiveDescriptor {
getFeaturePayload(config.features.verifiedTokens)?.api.endpoint, getFeaturePayload(config.features.verifiedTokens)?.api.endpoint,
getFeaturePayload(config.features.addressVerification)?.api.endpoint, getFeaturePayload(config.features.addressVerification)?.api.endpoint,
getFeaturePayload(config.features.nameService)?.api.endpoint, getFeaturePayload(config.features.nameService)?.api.endpoint,
getFeaturePayload(config.features.addressMetadata)?.api.endpoint,
marketplaceFeaturePayload && 'api' in marketplaceFeaturePayload ? marketplaceFeaturePayload.api.endpoint : '', marketplaceFeaturePayload && 'api' in marketplaceFeaturePayload ? marketplaceFeaturePayload.api.endpoint : '',
// chain RPC server // chain RPC server
...@@ -132,6 +133,10 @@ export function app(): CspDev.DirectiveDescriptor { ...@@ -132,6 +133,10 @@ export function app(): CspDev.DirectiveDescriptor {
'*', '*',
], ],
'frame-ancestors': [
KEY_WORDS.SELF,
],
...((() => { ...((() => {
if (!config.features.sentry.isEnabled) { if (!config.features.sentry.isEnabled) {
return {}; return {};
......
...@@ -44,14 +44,14 @@ class MyDocument extends Document { ...@@ -44,14 +44,14 @@ class MyDocument extends Document {
/> />
{ /* eslint-disable-next-line @next/next/no-sync-scripts */ } { /* eslint-disable-next-line @next/next/no-sync-scripts */ }
<script src="/envs.js"/> <script src="/assets/envs.js"/>
{ /* FAVICON */ } { /* FAVICON */ }
<link rel="icon" href="/favicon/favicon.ico" sizes="48x48"/> <link rel="icon" href="/assets/favicon/favicon.ico" sizes="48x48"/>
<link rel="icon" sizes="32x32" type="image/png" href="/favicon/favicon-32x32.png"/> <link rel="icon" sizes="32x32" type="image/png" href="/assets/favicon/favicon-32x32.png"/>
<link rel="icon" sizes="16x16" type="image/png"href="/favicon/favicon-16x16.png"/> <link rel="icon" sizes="16x16" type="image/png"href="/assets/favicon/favicon-16x16.png"/>
<link rel="apple-touch-icon" href="/favicon/apple-touch-icon-180x180.png"/> <link rel="apple-touch-icon" href="/assets/favicon/apple-touch-icon-180x180.png"/>
<link rel="mask-icon" href="/favicon/safari-pinned-tab.svg"/> <link rel="mask-icon" href="/assets/favicon/safari-pinned-tab.svg"/>
<link rel="preload" as="image" href={ svgSprite.href }/> <link rel="preload" as="image" href={ svgSprite.href }/>
</Head> </Head>
......
**Directories**
- `/icons` - Folder for SVG-sprite assets, generated at build time.
- `/static` - Folder for static assets that are consistent between app re-runs but may differ from one build version to another.
- `/assets` - Folder for dynamically generated assets during the app start, such as the favicon bundle, ENV variables file, and external app configurations.
\ No newline at end of file
<svg version="1.0" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 180 180"> <svg version="1.0" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 180 180">
<path d="M34.9 2.9c-2.9 2.9-2.9 3-2.9 15 0 6.8-.5 13.1-1.1 14.5-1.9 4-6.6 5.6-16.6 5.6C4.7 38 1.7 39.4.5 44.3.2 45.5.1 75.4.2 110.7l.3 64.2 2.3 2.3c2.1 2.1 3.3 2.3 14.6 2.6 14.8.4 17.9-.6 19.5-6.6.7-2.4 1.1-26.2 1.1-66.9V43.1l2.4-2.8c2.3-2.7 2.9-2.8 13.6-3.3 15-.7 15-.6 15-18.2v-13l-2.9-2.9C63.2 0 63.2 0 50.5 0S37.8 0 34.9 2.9zM111.8 1.5c-3.9 2.2-5.1 7.8-4.6 20.4.5 9.8.6 10.6 3.2 12.8 2.3 2 3.8 2.3 10.5 2.3 4.3 0 9.2.5 11 1.1 6.3 2.2 6.1.3 6.1 71.4v64.7l2.9 2.9c2.9 2.9 3 2.9 15.4 2.9 12 0 12.7-.1 15.6-2.6l3.1-2.6V42.9l-2.5-2.4c-2.2-2.2-3.2-2.5-10.5-2.5-8.1 0-14-1.6-15.6-4.1-.5-.8-1.1-7.6-1.4-15.1C144.3.3 144.6.6 127.2.2c-9.8-.1-13.3.1-15.4 1.3zM72.3 74.4l-2.8 2.4-.3 30.6-.3 30.5 3 3.3 2.9 3.3h13.1c12.9 0 13.1 0 15.8-2.8l2.8-2.7V76.8l-2.8-2.4C101 72.1 100.2 72 88 72s-13 .1-15.7 2.4z"/> <path d="M34.9 2.9c-2.9 2.9-2.9 3-2.9 15 0 6.8-.5 13.1-1.1 14.5-1.9 4-6.6 5.6-16.6 5.6C4.7 38 1.7 39.4.5 44.3.2 45.5.1 75.4.2 110.7l.3 64.2 2.3 2.3c2.1 2.1 3.3 2.3 14.6 2.6 14.8.4 17.9-.6 19.5-6.6.7-2.4 1.1-26.2 1.1-66.9V43.1l2.4-2.8c2.3-2.7 2.9-2.8 13.6-3.3 15-.7 15-.6 15-18.2v-13l-2.9-2.9C63.2 0 63.2 0 50.5 0S37.8 0 34.9 2.9zm76.9-1.4c-3.9 2.2-5.1 7.8-4.6 20.4.5 9.8.6 10.6 3.2 12.8 2.3 2 3.8 2.3 10.5 2.3 4.3 0 9.2.5 11 1.1 6.3 2.2 6.1.3 6.1 71.4v64.7l2.9 2.9c2.9 2.9 3 2.9 15.4 2.9 12 0 12.7-.1 15.6-2.6l3.1-2.6V42.9l-2.5-2.4c-2.2-2.2-3.2-2.5-10.5-2.5-8.1 0-14-1.6-15.6-4.1-.5-.8-1.1-7.6-1.4-15.1C144.3.3 144.6.6 127.2.2c-9.8-.1-13.3.1-15.4 1.3zM72.3 74.4l-2.8 2.4-.3 30.6-.3 30.5 3 3.3 2.9 3.3h13.1c12.9 0 13.1 0 15.8-2.8l2.8-2.7V76.8l-2.8-2.4C101 72.1 100.2 72 88 72s-13 .1-15.7 2.4z"/>
</svg> </svg>
import { formAnatomy as parts } from '@chakra-ui/anatomy'; import { formAnatomy as parts } from '@chakra-ui/anatomy';
import { createMultiStyleConfigHelpers } from '@chakra-ui/styled-system'; import { createMultiStyleConfigHelpers } from '@chakra-ui/styled-system';
import type { StyleFunctionProps } from '@chakra-ui/theme-tools'; import type { StyleFunctionProps } from '@chakra-ui/theme-tools';
import { getColor, mode } from '@chakra-ui/theme-tools';
import getDefaultFormColors from '../utils/getDefaultFormColors'; import getFormStyles from '../utils/getFormStyles';
import FancySelect from './FancySelect'; import FancySelect from './FancySelect';
import FormLabel from './FormLabel'; import FormLabel from './FormLabel';
import Input from './Input'; import Input from './Input';
...@@ -13,8 +12,7 @@ const { definePartsStyle, defineMultiStyleConfig } = ...@@ -13,8 +12,7 @@ const { definePartsStyle, defineMultiStyleConfig } =
createMultiStyleConfigHelpers(parts.keys); createMultiStyleConfigHelpers(parts.keys);
function getFloatingVariantStylesForSize(size: 'md' | 'lg', props: StyleFunctionProps) { function getFloatingVariantStylesForSize(size: 'md' | 'lg', props: StyleFunctionProps) {
const { theme } = props; const formStyles = getFormStyles(props);
const { focusPlaceholderColor, errorColor } = getDefaultFormColors(props);
const activeLabelStyles = { const activeLabelStyles = {
...FormLabel.variants?.floating?.(props)._focusWithin, ...FormLabel.variants?.floating?.(props)._focusWithin,
...@@ -63,12 +61,29 @@ function getFloatingVariantStylesForSize(size: 'md' | 'lg', props: StyleFunction ...@@ -63,12 +61,29 @@ function getFloatingVariantStylesForSize(size: 'md' | 'lg', props: StyleFunction
// label styles // label styles
label: FormLabel.sizes?.[size](props) || {}, label: FormLabel.sizes?.[size](props) || {},
'input:not(:placeholder-shown) + label, textarea:not(:placeholder-shown) + label': activeLabelStyles, '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, input[aria-invalid=true] + label,
textarea[aria-invalid=true] + label, textarea[aria-invalid=true] + label,
&[aria-invalid=true] label &[aria-invalid=true] label
`]: { `]: {
color: getColor(theme, errorColor), color: formStyles.placeholder.error.color,
},
[`
input[disabled] + label,
textarea[disabled] + label,
&[aria-disabled=true] label
`]: {
color: formStyles.placeholder.disabled.color,
}, },
// input styles // input styles
...@@ -79,31 +94,24 @@ function getFloatingVariantStylesForSize(size: 'md' | 'lg', props: StyleFunction ...@@ -79,31 +94,24 @@ function getFloatingVariantStylesForSize(size: 'md' | 'lg', props: StyleFunction
padding: inputPx, padding: inputPx,
}, },
'input:not(:placeholder-shown), textarea:not(:placeholder-shown)': activeInputStyles, 'input:not(:placeholder-shown), textarea:not(:placeholder-shown)': activeInputStyles,
[`
input[disabled] + label,
&[aria-disabled=true] label
`]: {
backgroundColor: 'transparent',
},
// in textarea bg of label could not be transparent; it should match the background color of input but without alpha
// so we have to use non-standard colors here
'textarea[disabled] + label': {
backgroundColor: mode('#ececec', '#232425')(props),
},
'textarea[disabled] + label[data-in-modal=true]': {
backgroundColor: mode('#ececec', '#292b34')(props),
},
// indicator styles // indicator styles
'input:not(:placeholder-shown) + label .chakra-form__required-indicator, textarea:not(:placeholder-shown) + label .chakra-form__required-indicator': { 'input:not(:placeholder-shown) + label .chakra-form__required-indicator, textarea:not(:placeholder-shown) + label .chakra-form__required-indicator': {
color: getColor(theme, focusPlaceholderColor), color: formStyles.placeholder.default.color,
}, },
[` [`
input[aria-invalid=true] + label .chakra-form__required-indicator, input[aria-invalid=true] + label .chakra-form__required-indicator,
textarea[aria-invalid=true] + label .chakra-form__required-indicator, textarea[aria-invalid=true] + label .chakra-form__required-indicator,
&[aria-invalid=true] .chakra-form__required-indicator &[aria-invalid=true] .chakra-form__required-indicator
`]: { `]: {
color: getColor(theme, errorColor), 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,
}, },
}, },
}; };
......
...@@ -65,6 +65,19 @@ test.describe('floating label size md +@dark-mode', () => { ...@@ -65,6 +65,19 @@ test.describe('floating label size md +@dark-mode', () => {
await expect(component).toHaveScreenshot(); await expect(component).toHaveScreenshot();
}); });
test('filled read-only', async({ mount }) => {
const component = await mount(
<TestApp>
<FormControl variant="floating" id="name" isRequired size="md">
<Input required value="foo" isReadOnly/>
<FormLabel>Smart contract / Address (0x...)</FormLabel>
</FormControl>
</TestApp>,
);
await expect(component).toHaveScreenshot();
});
test('filled error', async({ mount }) => { test('filled error', async({ mount }) => {
const component = await mount( const component = await mount(
<TestApp> <TestApp>
......
import { defineStyle, defineStyleConfig } from '@chakra-ui/styled-system'; import { defineStyle, defineStyleConfig } from '@chakra-ui/styled-system';
import { getColor } from '@chakra-ui/theme-tools';
import getDefaultFormColors from '../utils/getDefaultFormColors'; import getFormStyles from '../utils/getFormStyles';
const baseStyle = defineStyle({ const baseStyle = defineStyle({
display: 'flex', display: 'flex',
...@@ -13,14 +12,12 @@ const baseStyle = defineStyle({ ...@@ -13,14 +12,12 @@ const baseStyle = defineStyle({
transitionDuration: 'normal', transitionDuration: 'normal',
opacity: 1, opacity: 1,
_disabled: { _disabled: {
opacity: 0.4, opacity: 0.2,
}, },
}); });
const variantFloating = defineStyle((props) => { const variantFloating = defineStyle((props) => {
const { theme, backgroundColor } = props; const formStyles = getFormStyles(props);
const { focusPlaceholderColor } = getDefaultFormColors(props);
const bc = backgroundColor || 'transparent';
return { return {
left: '2px', left: '2px',
...@@ -29,8 +26,8 @@ const variantFloating = defineStyle((props) => { ...@@ -29,8 +26,8 @@ const variantFloating = defineStyle((props) => {
position: 'absolute', position: 'absolute',
borderRadius: 'base', borderRadius: 'base',
boxSizing: 'border-box', boxSizing: 'border-box',
color: 'gray.500', color: formStyles.placeholder.default.color,
backgroundColor: 'transparent', backgroundColor: props.bgColor || props.backgroundColor || 'transparent',
pointerEvents: 'none', pointerEvents: 'none',
margin: 0, margin: 0,
transformOrigin: 'top left', transformOrigin: 'top left',
...@@ -39,8 +36,8 @@ const variantFloating = defineStyle((props) => { ...@@ -39,8 +36,8 @@ const variantFloating = defineStyle((props) => {
whiteSpace: 'nowrap', whiteSpace: 'nowrap',
textOverflow: 'ellipsis', textOverflow: 'ellipsis',
_focusWithin: { _focusWithin: {
backgroundColor: bc, backgroundColor: props.bgColor || props.backgroundColor || 'transparent',
color: getColor(theme, focusPlaceholderColor), color: formStyles.placeholder.default.color,
fontSize: 'xs', fontSize: 'xs',
lineHeight: '16px', lineHeight: '16px',
borderTopRightRadius: 'none', borderTopRightRadius: 'none',
...@@ -70,7 +67,7 @@ const sizes = { ...@@ -70,7 +67,7 @@ const sizes = {
return { return {
fontSize: 'md', fontSize: 'md',
lineHeight: '24px', lineHeight: '24px',
padding: '28px 4px 28px 24px', padding: '26px 4px 26px 24px',
right: '22px', right: '22px',
_focusWithin: { _focusWithin: {
padding: '16px 0 2px 24px', padding: '16px 0 2px 24px',
......
...@@ -29,6 +29,20 @@ const size = { ...@@ -29,6 +29,20 @@ const size = {
h: '40px', h: '40px',
borderRadius: 'base', 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({ md: defineStyle({
fontSize: 'md', fontSize: 'md',
lineHeight: '20px', lineHeight: '20px',
...@@ -71,6 +85,10 @@ const sizes = { ...@@ -71,6 +85,10 @@ const sizes = {
field: size.sm, field: size.sm,
addon: size.sm, addon: size.sm,
}), }),
sm_md: definePartsStyle({
field: size.sm_md,
addon: size.sm_md,
}),
md: definePartsStyle({ md: definePartsStyle({
field: size.md, field: size.md,
addon: size.md, addon: size.md,
......
...@@ -10,11 +10,11 @@ import { runIfFn } from '@chakra-ui/utils'; ...@@ -10,11 +10,11 @@ import { runIfFn } from '@chakra-ui/utils';
const { defineMultiStyleConfig, definePartsStyle } = const { defineMultiStyleConfig, definePartsStyle } =
createMultiStyleConfigHelpers(parts.keys); createMultiStyleConfigHelpers(parts.keys);
const baseStyleDialog = defineStyle((props) => { const baseStyleDialog = defineStyle(() => {
return { return {
padding: 8, padding: 8,
borderRadius: 'lg', borderRadius: 'lg',
bg: mode('white', 'gray.900')(props), bg: 'dialog_bg',
margin: 'auto', margin: 'auto',
}; };
}); });
...@@ -61,7 +61,7 @@ const baseStyleOverlay = defineStyle({ ...@@ -61,7 +61,7 @@ const baseStyleOverlay = defineStyle({
}); });
const baseStyle = definePartsStyle((props) => ({ const baseStyle = definePartsStyle((props) => ({
dialog: runIfFn(baseStyleDialog, props), dialog: runIfFn(baseStyleDialog),
dialogContainer: baseStyleDialogContainer, dialogContainer: baseStyleDialogContainer,
header: runIfFn(baseStyleHeader, props), header: runIfFn(baseStyleHeader, props),
......
import { Textarea as TextareaComponent } from '@chakra-ui/react'; import { Textarea as TextareaComponent } from '@chakra-ui/react';
import { defineStyle, defineStyleConfig } from '@chakra-ui/styled-system'; import { defineStyle, defineStyleConfig } from '@chakra-ui/styled-system';
import { mode } from '@chakra-ui/theme-tools';
import getOutlinedFieldStyles from '../utils/getOutlinedFieldStyles'; import getOutlinedFieldStyles from '../utils/getOutlinedFieldStyles';
const variantFilledInactive = defineStyle((props) => {
return {
// https://bugs.chromium.org/p/chromium/issues/detail?id=1362573
// there is a problem with scrollbar color in chromium
// so blackAlpha.50 here is replaced with #f5f5f6
// and whiteAlpha.50 is replaced with #1a1b1b
// bgColor: mode('blackAlpha.50', 'whiteAlpha.50')(props),
bgColor: mode('#f5f5f6', '#1a1b1b')(props),
};
});
const sizes = { const sizes = {
md: defineStyle({ md: defineStyle({
fontSize: 'md', fontSize: 'md',
...@@ -38,7 +24,6 @@ const Textarea = defineStyleConfig({ ...@@ -38,7 +24,6 @@ const Textarea = defineStyleConfig({
sizes, sizes,
variants: { variants: {
outline: defineStyle(getOutlinedFieldStyles), outline: defineStyle(getOutlinedFieldStyles),
filledInactive: variantFilledInactive,
}, },
defaultProps: { defaultProps: {
variant: 'outline', variant: 'outline',
......
...@@ -23,6 +23,10 @@ const semanticTokens = { ...@@ -23,6 +23,10 @@ const semanticTokens = {
'default': 'red.400', 'default': 'red.400',
_dark: 'red.300', _dark: 'red.300',
}, },
dialog_bg: {
'default': 'white',
_dark: 'gray.900',
},
}, },
shadows: { shadows: {
action_bar: '0 4px 4px -4px rgb(0 0 0 / 10%), 0 2px 4px -4px rgb(0 0 0 / 6%)', action_bar: '0 4px 4px -4px rgb(0 0 0 / 10%), 0 2px 4px -4px rgb(0 0 0 / 6%)',
......
import type { StyleFunctionProps } from '@chakra-ui/theme-tools';
import { mode } from '@chakra-ui/theme-tools';
export default function getDefaultFormColors(props: StyleFunctionProps) {
const { focusBorderColor: fc, errorBorderColor: ec, filledBorderColor: flc } = props;
return {
focusBorderColor: fc || mode('blue.500', 'blue.300')(props),
focusPlaceholderColor: fc || 'gray.500',
errorColor: ec || mode('red.400', 'red.300')(props),
filledColor: flc || mode('gray.300', 'gray.600')(props),
};
}
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),
},
},
};
}
import type { StyleFunctionProps } from '@chakra-ui/theme-tools'; import type { StyleFunctionProps } from '@chakra-ui/theme-tools';
import { mode, getColor } from '@chakra-ui/theme-tools'; import { mode } from '@chakra-ui/theme-tools';
import getDefaultFormColors from './getDefaultFormColors';
import getDefaultTransitionProps from './getDefaultTransitionProps'; import getDefaultTransitionProps from './getDefaultTransitionProps';
import getFormStyles from './getFormStyles';
export default function getOutlinedFieldStyles(props: StyleFunctionProps) { export default function getOutlinedFieldStyles(props: StyleFunctionProps) {
const { theme, borderColor } = props; const formStyles = getFormStyles(props);
const { focusBorderColor, errorColor } = getDefaultFormColors(props);
const transitionProps = getDefaultTransitionProps(); const transitionProps = getDefaultTransitionProps();
return { return {
border: '2px solid', border: '2px solid',
// filled input // filled input
backgroundColor: 'transparent', ...formStyles.input.filled,
borderColor: mode('gray.300', 'gray.600')(props),
...transitionProps, ...transitionProps,
_hover: { _hover: {
borderColor: mode('gray.200', 'gray.500')(props), ...formStyles.input.hover,
}, },
_readOnly: { _readOnly: {
boxShadow: 'none !important', boxShadow: 'none !important',
userSelect: 'all', userSelect: 'all',
pointerEvents: 'none',
...formStyles.input.readOnly,
_hover: {
...formStyles.input.readOnly,
},
_focus: {
...formStyles.input.readOnly,
},
}, },
_disabled: { _disabled: {
opacity: 1, ...formStyles.input.disabled,
backgroundColor: mode('blackAlpha.200', 'whiteAlpha.200')(props),
borderColor: 'transparent',
cursor: 'not-allowed', cursor: 'not-allowed',
_hover: {
borderColor: 'transparent',
},
':-webkit-autofill': { ':-webkit-autofill': {
// background color for disabled input which value was selected from browser autocomplete popup // 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`, '-webkit-box-shadow': `0 0 0px 1000px ${ mode('rgba(16, 17, 18, 0.08)', 'rgba(255, 255, 255, 0.08)')(props) } inset`,
}, },
}, },
_invalid: { _invalid: {
borderColor: getColor(theme, errorColor), ...formStyles.input.error,
boxShadow: `none`, boxShadow: `none`,
_placeholder: {
color: formStyles.placeholder.error.color,
},
}, },
_focusVisible: { _focusVisible: {
...formStyles.input.focus,
zIndex: 1, zIndex: 1,
borderColor: getColor(theme, focusBorderColor),
boxShadow: 'md', boxShadow: 'md',
}, },
_placeholder: { _placeholder: {
color: mode('blackAlpha.600', 'whiteAlpha.600')(props), color: formStyles.placeholder.default.color,
}, },
// not filled input // not filled input
':placeholder-shown:not(:focus-visible):not(:hover):not([aria-invalid=true])': { borderColor: borderColor || mode('gray.100', 'gray.700')(props) }, ':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 filled input with type="date"
':not(:placeholder-shown)[value=""]:not(:focus-visible):not(:hover):not([aria-invalid=true])': { ':not(:placeholder-shown)[value=""]:not(:focus-visible):not(:hover):not([aria-invalid=true]):not([aria-readonly=true])': {
borderColor: borderColor || mode('gray.100', 'gray.700')(props), ...formStyles.input.empty,
color: 'gray.500',
}, },
':-webkit-autofill': { transition: 'background-color 5000s ease-in-out 0s' }, ':-webkit-autofill': { transition: 'background-color 5000s ease-in-out 0s' },
......
...@@ -17,7 +17,7 @@ fi ...@@ -17,7 +17,7 @@ fi
# download assets for the running instance # download assets for the running instance
dotenv \ dotenv \
-e $config_file \ -e $config_file \
-- bash -c './deploy/scripts/download_assets.sh ./public/assets' -- bash -c './deploy/scripts/download_assets.sh ./public/assets/configs'
yarn svg:build-sprite yarn svg:build-sprite
echo "" echo ""
......
...@@ -6,7 +6,7 @@ dotenv \ ...@@ -6,7 +6,7 @@ dotenv \
-e .env.local \ -e .env.local \
-e .env.development \ -e .env.development \
-e .env \ -e .env \
-- bash -c './deploy/scripts/download_assets.sh ./public/assets' -- bash -c './deploy/scripts/download_assets.sh ./public/assets/configs'
yarn svg:build-sprite yarn svg:build-sprite
echo "" echo ""
...@@ -20,5 +20,5 @@ dotenv \ ...@@ -20,5 +20,5 @@ dotenv \
-e .env.local \ -e .env.local \
-e .env.development \ -e .env.development \
-e .env \ -e .env \
-- bash -c './deploy/scripts/make_envs_script.sh && next dev -- -p $NEXT_PUBLIC_APP_PORT' | -- bash -c './deploy/scripts/make_envs_script.sh && next dev -p $NEXT_PUBLIC_APP_PORT' |
pino-pretty pino-pretty
\ No newline at end of file
secrets_file="./configs/envs/.env.secrets" secrets_file="./configs/envs/.env.secrets"
favicon_folder="./public/favicon/" favicon_folder="./public/assets/favicon/"
master_url="https://raw.githubusercontent.com/blockscout/frontend/main/tools/scripts/favicon.svg" master_url="https://raw.githubusercontent.com/blockscout/frontend/main/tools/scripts/favicon.svg"
if [ ! -f "$secrets_file" ]; then if [ ! -f "$secrets_file" ]; then
......
...@@ -14,7 +14,7 @@ import useSocketMessage from 'lib/socket/useSocketMessage'; ...@@ -14,7 +14,7 @@ import useSocketMessage from 'lib/socket/useSocketMessage';
import { currencyUnits } from 'lib/units'; import { currencyUnits } from 'lib/units';
import { BLOCK } from 'stubs/block'; import { BLOCK } from 'stubs/block';
import { generateListStub } from 'stubs/utils'; import { generateListStub } from 'stubs/utils';
import ActionBar from 'ui/shared/ActionBar'; import ActionBar, { ACTION_BAR_HEIGHT_DESKTOP } from 'ui/shared/ActionBar';
import DataListDisplay from 'ui/shared/DataListDisplay'; import DataListDisplay from 'ui/shared/DataListDisplay';
import Pagination from 'ui/shared/pagination/Pagination'; import Pagination from 'ui/shared/pagination/Pagination';
import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages'; import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages';
...@@ -96,7 +96,7 @@ const AddressBlocksValidated = ({ scrollRef, shouldRender = true }: Props) => { ...@@ -96,7 +96,7 @@ const AddressBlocksValidated = ({ scrollRef, shouldRender = true }: Props) => {
{ socketAlert && <SocketAlert mb={ 6 }/> } { socketAlert && <SocketAlert mb={ 6 }/> }
<Hide below="lg" ssr={ false }> <Hide below="lg" ssr={ false }>
<Table variant="simple" size="sm"> <Table variant="simple" size="sm">
<Thead top={ query.pagination.isVisible ? 80 : 0 }> <Thead top={ query.pagination.isVisible ? ACTION_BAR_HEIGHT_DESKTOP : 0 }>
<Tr> <Tr>
<Th width="17%">Block</Th> <Th width="17%">Block</Th>
<Th width="17%">Age</Th> <Th width="17%">Age</Th>
......
...@@ -20,7 +20,7 @@ import useSocketChannel from 'lib/socket/useSocketChannel'; ...@@ -20,7 +20,7 @@ import useSocketChannel from 'lib/socket/useSocketChannel';
import useSocketMessage from 'lib/socket/useSocketMessage'; import useSocketMessage from 'lib/socket/useSocketMessage';
import { TOKEN_TYPE_IDS } from 'lib/token/tokenTypes'; import { TOKEN_TYPE_IDS } from 'lib/token/tokenTypes';
import { getTokenTransfersStub } from 'stubs/token'; import { getTokenTransfersStub } from 'stubs/token';
import ActionBar from 'ui/shared/ActionBar'; import ActionBar, { ACTION_BAR_HEIGHT_DESKTOP } from 'ui/shared/ActionBar';
import DataListDisplay from 'ui/shared/DataListDisplay'; import DataListDisplay from 'ui/shared/DataListDisplay';
import * as TokenEntity from 'ui/shared/entities/token/TokenEntity'; import * as TokenEntity from 'ui/shared/entities/token/TokenEntity';
import HashStringShorten from 'ui/shared/HashStringShorten'; import HashStringShorten from 'ui/shared/HashStringShorten';
...@@ -204,7 +204,7 @@ const AddressTokenTransfers = ({ scrollRef, overloadCount = OVERLOAD_COUNT, shou ...@@ -204,7 +204,7 @@ const AddressTokenTransfers = ({ scrollRef, overloadCount = OVERLOAD_COUNT, shou
data={ data?.items } data={ data?.items }
baseAddress={ currentAddress } baseAddress={ currentAddress }
showTxInfo showTxInfo
top={ isActionBarHidden ? 0 : 80 } top={ isActionBarHidden ? 0 : ACTION_BAR_HEIGHT_DESKTOP }
enableTimeIncrement enableTimeIncrement
showSocketInfo={ pagination.page === 1 && !tokenFilter } showSocketInfo={ pagination.page === 1 && !tokenFilter }
socketInfoAlert={ socketAlert } socketInfoAlert={ socketAlert }
......
...@@ -16,7 +16,7 @@ import useSocketChannel from 'lib/socket/useSocketChannel'; ...@@ -16,7 +16,7 @@ import useSocketChannel from 'lib/socket/useSocketChannel';
import useSocketMessage from 'lib/socket/useSocketMessage'; import useSocketMessage from 'lib/socket/useSocketMessage';
import { TX } from 'stubs/tx'; import { TX } from 'stubs/tx';
import { generateListStub } from 'stubs/utils'; import { generateListStub } from 'stubs/utils';
import ActionBar from 'ui/shared/ActionBar'; import ActionBar, { ACTION_BAR_HEIGHT_DESKTOP } from 'ui/shared/ActionBar';
import Pagination from 'ui/shared/pagination/Pagination'; import Pagination from 'ui/shared/pagination/Pagination';
import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages'; import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages';
import getSortParamsFromValue from 'ui/shared/sort/getSortParamsFromValue'; import getSortParamsFromValue from 'ui/shared/sort/getSortParamsFromValue';
...@@ -199,7 +199,7 @@ const AddressTxs = ({ scrollRef, overloadCount = OVERLOAD_COUNT, shouldRender = ...@@ -199,7 +199,7 @@ const AddressTxs = ({ scrollRef, overloadCount = OVERLOAD_COUNT, shouldRender =
showSocketInfo={ addressTxsQuery.pagination.page === 1 } showSocketInfo={ addressTxsQuery.pagination.page === 1 }
socketInfoAlert={ socketAlert } socketInfoAlert={ socketAlert }
socketInfoNum={ newItemsCount } socketInfoNum={ newItemsCount }
top={ 80 } top={ ACTION_BAR_HEIGHT_DESKTOP }
sorting={ sort } sorting={ sort }
setSort={ setSort } setSort={ setSort }
/> />
......
...@@ -6,7 +6,7 @@ import useIsMounted from 'lib/hooks/useIsMounted'; ...@@ -6,7 +6,7 @@ import useIsMounted from 'lib/hooks/useIsMounted';
import getQueryParamString from 'lib/router/getQueryParamString'; import getQueryParamString from 'lib/router/getQueryParamString';
import { generateListStub } from 'stubs/utils'; import { generateListStub } from 'stubs/utils';
import { WITHDRAWAL } from 'stubs/withdrawals'; import { WITHDRAWAL } from 'stubs/withdrawals';
import ActionBar from 'ui/shared/ActionBar'; import ActionBar, { ACTION_BAR_HEIGHT_DESKTOP } from 'ui/shared/ActionBar';
import DataListDisplay from 'ui/shared/DataListDisplay'; import DataListDisplay from 'ui/shared/DataListDisplay';
import Pagination from 'ui/shared/pagination/Pagination'; import Pagination from 'ui/shared/pagination/Pagination';
import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages'; import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages';
...@@ -52,7 +52,12 @@ const AddressWithdrawals = ({ scrollRef, shouldRender = true }: Props) => { ...@@ -52,7 +52,12 @@ const AddressWithdrawals = ({ scrollRef, shouldRender = true }: Props) => {
)) } )) }
</Show> </Show>
<Hide below="lg" ssr={ false }> <Hide below="lg" ssr={ false }>
<BeaconChainWithdrawalsTable items={ data.items } view="address" top={ pagination.isVisible ? 80 : 0 } isLoading={ isPlaceholderData }/> <BeaconChainWithdrawalsTable
items={ data.items }
view="address"
top={ pagination.isVisible ? ACTION_BAR_HEIGHT_DESKTOP : 0 }
isLoading={ isPlaceholderData }
/>
</Hide> </Hide>
</> </>
) : null ; ) : null ;
......
...@@ -7,7 +7,7 @@ import type { PaginationParams } from 'ui/shared/pagination/types'; ...@@ -7,7 +7,7 @@ import type { PaginationParams } from 'ui/shared/pagination/types';
import type { ResourceError } from 'lib/api/resources'; import type { ResourceError } from 'lib/api/resources';
import { currencyUnits } from 'lib/units'; import { currencyUnits } from 'lib/units';
import ActionBar from 'ui/shared/ActionBar'; import ActionBar, { ACTION_BAR_HEIGHT_DESKTOP } from 'ui/shared/ActionBar';
import DataListDisplay from 'ui/shared/DataListDisplay'; import DataListDisplay from 'ui/shared/DataListDisplay';
import Pagination from 'ui/shared/pagination/Pagination'; import Pagination from 'ui/shared/pagination/Pagination';
import { default as Thead } from 'ui/shared/TheadSticky'; import { default as Thead } from 'ui/shared/TheadSticky';
...@@ -27,7 +27,7 @@ const AddressCoinBalanceHistory = ({ query }: Props) => { ...@@ -27,7 +27,7 @@ const AddressCoinBalanceHistory = ({ query }: Props) => {
<> <>
<Hide below="lg" ssr={ false }> <Hide below="lg" ssr={ false }>
<Table variant="simple" size="sm"> <Table variant="simple" size="sm">
<Thead top={ query.pagination.isVisible ? 80 : 0 }> <Thead top={ query.pagination.isVisible ? ACTION_BAR_HEIGHT_DESKTOP : 0 }>
<Tr> <Tr>
<Th width="20%">Block</Th> <Th width="20%">Block</Th>
<Th width="20%">Txn</Th> <Th width="20%">Txn</Th>
......
...@@ -128,7 +128,6 @@ const ContractSourceCode = ({ address, implementationAddress }: Props) => { ...@@ -128,7 +128,6 @@ const ContractSourceCode = ({ address, implementationAddress }: Props) => {
size="xs" size="xs"
value={ sourceType } value={ sourceType }
onChange={ handleSelectChange } onChange={ handleSelectChange }
focusBorderColor="none"
w="auto" w="auto"
fontWeight={ 600 } fontWeight={ 600 }
borderRadius="base" borderRadius="base"
......
...@@ -19,7 +19,7 @@ const AddressIntTxsTable = ({ data, currentAddress, isLoading }: Props) => { ...@@ -19,7 +19,7 @@ const AddressIntTxsTable = ({ data, currentAddress, isLoading }: Props) => {
return ( return (
<AddressHighlightProvider> <AddressHighlightProvider>
<Table variant="simple" size="sm"> <Table variant="simple" size="sm">
<Thead top={ 80 }> <Thead top={ 68 }>
<Tr> <Tr>
<Th width="15%">Parent txn hash</Th> <Th width="15%">Parent txn hash</Th>
<Th width="15%">Type</Th> <Th width="15%">Type</Th>
......
...@@ -24,7 +24,6 @@ interface Props { ...@@ -24,7 +24,6 @@ interface Props {
const TokenSelectMenu = ({ erc20sort, erc1155sort, erc404sort, filteredData, onInputChange, onSortClick, searchTerm }: Props) => { const TokenSelectMenu = ({ erc20sort, erc1155sort, erc404sort, filteredData, onInputChange, onSortClick, searchTerm }: Props) => {
const searchIconColor = useColorModeValue('blackAlpha.600', 'whiteAlpha.600'); const searchIconColor = useColorModeValue('blackAlpha.600', 'whiteAlpha.600');
const inputBorderColor = useColorModeValue('blackAlpha.100', 'whiteAlpha.200');
const hasFilteredResult = _sumBy(Object.values(filteredData), ({ items }) => items.length) > 0; const hasFilteredResult = _sumBy(Object.values(filteredData), ({ items }) => items.length) > 0;
...@@ -39,7 +38,7 @@ const TokenSelectMenu = ({ erc20sort, erc1155sort, erc404sort, filteredData, onI ...@@ -39,7 +38,7 @@ const TokenSelectMenu = ({ erc20sort, erc1155sort, erc404sort, filteredData, onI
placeholder="Search by token name" placeholder="Search by token name"
ml="1px" ml="1px"
onChange={ onInputChange } onChange={ onInputChange }
borderColor={ inputBorderColor } bgColor="dialog_bg"
/> />
</InputGroup> </InputGroup>
<Flex flexDir="column" rowGap={ 6 }> <Flex flexDir="column" rowGap={ 6 }>
......
import { FormControl, Input, useColorModeValue } from '@chakra-ui/react'; import { FormControl, Input } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import type { Control, ControllerRenderProps, FormState } from 'react-hook-form'; import type { Control, ControllerRenderProps, FormState } from 'react-hook-form';
import { Controller } from 'react-hook-form'; import { Controller } from 'react-hook-form';
...@@ -15,13 +15,11 @@ interface Props { ...@@ -15,13 +15,11 @@ interface Props {
} }
const AddressVerificationFieldAddress = ({ formState, control }: Props) => { const AddressVerificationFieldAddress = ({ formState, control }: Props) => {
const backgroundColor = useColorModeValue('white', 'gray.900');
const renderControl = React.useCallback(({ field }: {field: ControllerRenderProps<Fields, 'address'>}) => { const renderControl = React.useCallback(({ field }: {field: ControllerRenderProps<Fields, 'address'>}) => {
const error = 'address' in formState.errors ? formState.errors.address : undefined; const error = 'address' in formState.errors ? formState.errors.address : undefined;
return ( return (
<FormControl variant="floating" id={ field.name } isRequired size="md" backgroundColor={ backgroundColor } mt={ 8 }> <FormControl variant="floating" id={ field.name } isRequired size="md" bgColor="dialog_bg" mt={ 8 }>
<Input <Input
{ ...field } { ...field }
required required
...@@ -29,11 +27,12 @@ const AddressVerificationFieldAddress = ({ formState, control }: Props) => { ...@@ -29,11 +27,12 @@ const AddressVerificationFieldAddress = ({ formState, control }: Props) => {
maxLength={ ADDRESS_LENGTH } maxLength={ ADDRESS_LENGTH }
isDisabled={ formState.isSubmitting } isDisabled={ formState.isSubmitting }
autoComplete="off" autoComplete="off"
bgColor="dialog_bg"
/> />
<InputPlaceholder text="Smart contract address (0x...)" error={ error }/> <InputPlaceholder text="Smart contract address (0x...)" error={ error }/>
</FormControl> </FormControl>
); );
}, [ formState.errors, formState.isSubmitting, backgroundColor ]); }, [ formState.errors, formState.isSubmitting ]);
return ( return (
<Controller <Controller
......
import { FormControl, Textarea, useColorModeValue } from '@chakra-ui/react'; import { FormControl, Textarea } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import type { Control, ControllerRenderProps, FormState } from 'react-hook-form'; import type { Control, ControllerRenderProps, FormState } from 'react-hook-form';
import { Controller } from 'react-hook-form'; import { Controller } from 'react-hook-form';
...@@ -15,25 +15,24 @@ interface Props { ...@@ -15,25 +15,24 @@ interface Props {
} }
const AddressVerificationFieldMessage = ({ formState, control }: Props) => { const AddressVerificationFieldMessage = ({ formState, control }: Props) => {
const backgroundColor = useColorModeValue('white', 'gray.900');
const renderControl = React.useCallback(({ field }: {field: ControllerRenderProps<Fields, 'message'>}) => { const renderControl = React.useCallback(({ field }: {field: ControllerRenderProps<Fields, 'message'>}) => {
const error = 'message' in formState.errors ? formState.errors.message : undefined; const error = 'message' in formState.errors ? formState.errors.message : undefined;
return ( return (
<FormControl variant="floating" id={ field.name } isRequired size="md" backgroundColor={ backgroundColor }> <FormControl variant="floating" id={ field.name } isRequired size="md" bgColor="dialog_bg">
<Textarea <Textarea
{ ...field } { ...field }
required required
isInvalid={ Boolean(error) } isInvalid={ Boolean(error) }
isDisabled isReadOnly
autoComplete="off" autoComplete="off"
maxH={{ base: '140px', lg: '80px' }} maxH={{ base: '140px', lg: '80px' }}
bgColor="dialog_bg"
/> />
<InputPlaceholder text="Message to sign" error={ error } isInModal/> <InputPlaceholder text="Message to sign" error={ error }/>
</FormControl> </FormControl>
); );
}, [ formState.errors, backgroundColor ]); }, [ formState.errors ]);
return ( return (
<Controller <Controller
......
import { FormControl, Input, useColorModeValue } from '@chakra-ui/react'; import { FormControl, Input } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import type { Control, ControllerRenderProps, FormState } from 'react-hook-form'; import type { Control, ControllerRenderProps, FormState } from 'react-hook-form';
import { Controller } from 'react-hook-form'; import { Controller } from 'react-hook-form';
...@@ -16,24 +16,23 @@ interface Props { ...@@ -16,24 +16,23 @@ interface Props {
} }
const AddressVerificationFieldSignature = ({ formState, control }: Props) => { const AddressVerificationFieldSignature = ({ formState, control }: Props) => {
const backgroundColor = useColorModeValue('white', 'gray.900');
const renderControl = React.useCallback(({ field }: {field: ControllerRenderProps<Fields, 'signature'>}) => { const renderControl = React.useCallback(({ field }: {field: ControllerRenderProps<Fields, 'signature'>}) => {
const error = 'signature' in formState.errors ? formState.errors.signature : undefined; const error = 'signature' in formState.errors ? formState.errors.signature : undefined;
return ( return (
<FormControl variant="floating" id={ field.name } isRequired size="md" backgroundColor={ backgroundColor }> <FormControl variant="floating" id={ field.name } isRequired size="md" bgColor="dialog_bg">
<Input <Input
{ ...field } { ...field }
required required
isInvalid={ Boolean(error) } isInvalid={ Boolean(error) }
isDisabled={ formState.isSubmitting } isDisabled={ formState.isSubmitting }
autoComplete="off" autoComplete="off"
bgColor="dialog_bg"
/> />
<InputPlaceholder text="Signature hash" error={ error }/> <InputPlaceholder text="Signature hash" error={ error }/>
</FormControl> </FormControl>
); );
}, [ formState.errors, formState.isSubmitting, backgroundColor ]); }, [ formState.errors, formState.isSubmitting ]);
return ( return (
<Controller <Controller
......
...@@ -105,7 +105,7 @@ const AddressVerificationStepAddress = ({ defaultAddress, onContinue }: Props) = ...@@ -105,7 +105,7 @@ const AddressVerificationStepAddress = ({ defaultAddress, onContinue }: Props) =
{ rootError && <Alert status="warning" mt={ 3 }>{ rootError }</Alert> } { rootError && <Alert status="warning" mt={ 3 }>{ rootError }</Alert> }
<AddressVerificationFieldAddress formState={ formState } control={ control }/> <AddressVerificationFieldAddress formState={ formState } control={ control }/>
<Flex alignItems={{ base: 'flex-start', lg: 'center' }} mt={ 8 } columnGap={ 5 } rowGap={ 2 } flexDir={{ base: 'column', lg: 'row' }}> <Flex alignItems={{ base: 'flex-start', lg: 'center' }} mt={ 8 } columnGap={ 5 } rowGap={ 2 } flexDir={{ base: 'column', lg: 'row' }}>
<Button size="lg" type="submit" isDisabled={ formState.isSubmitting } flexShrink={ 0 }> <Button size="lg" type="submit" isLoading={ formState.isSubmitting } loadingText="Continue" flexShrink={ 0 }>
Continue Continue
</Button> </Button>
<AdminSupportText/> <AdminSupportText/>
......
...@@ -4,7 +4,6 @@ import { ...@@ -4,7 +4,6 @@ import {
FormControl, FormControl,
FormLabel, FormLabel,
Input, Input,
useColorModeValue,
} from '@chakra-ui/react'; } from '@chakra-ui/react';
import { useMutation, useQueryClient } from '@tanstack/react-query'; import { useMutation, useQueryClient } from '@tanstack/react-query';
import React, { useCallback } from 'react'; import React, { useCallback } from 'react';
...@@ -42,7 +41,6 @@ const ApiKeyForm: React.FC<Props> = ({ data, onClose, setAlertVisible }) => { ...@@ -42,7 +41,6 @@ const ApiKeyForm: React.FC<Props> = ({ data, onClose, setAlertVisible }) => {
}); });
const apiFetch = useApiFetch(); const apiFetch = useApiFetch();
const queryClient = useQueryClient(); const queryClient = useQueryClient();
const formBackgroundColor = useColorModeValue('white', 'gray.900');
const updateApiKey = (data: Inputs) => { const updateApiKey = (data: Inputs) => {
const body = { name: data.name }; const body = { name: data.name };
...@@ -102,25 +100,27 @@ const ApiKeyForm: React.FC<Props> = ({ data, onClose, setAlertVisible }) => { ...@@ -102,25 +100,27 @@ const ApiKeyForm: React.FC<Props> = ({ data, onClose, setAlertVisible }) => {
<FormControl variant="floating" id="address"> <FormControl variant="floating" id="address">
<Input <Input
{ ...field } { ...field }
isDisabled={ true } bgColor="dialog_bg"
isReadOnly
/> />
<FormLabel data-in-modal="true">Auto-generated API key token</FormLabel> <FormLabel>Auto-generated API key token</FormLabel>
</FormControl> </FormControl>
); );
}, []); }, []);
const renderNameInput = useCallback(({ field }: {field: ControllerRenderProps<Inputs, 'name'>}) => { const renderNameInput = useCallback(({ field }: {field: ControllerRenderProps<Inputs, 'name'>}) => {
return ( return (
<FormControl variant="floating" id="name" isRequired backgroundColor={ formBackgroundColor }> <FormControl variant="floating" id="name" isRequired bgColor="dialog_bg">
<Input <Input
{ ...field } { ...field }
isInvalid={ Boolean(errors.name) } isInvalid={ Boolean(errors.name) }
maxLength={ NAME_MAX_LENGTH } maxLength={ NAME_MAX_LENGTH }
bgColor="dialog_bg"
/> />
<InputPlaceholder text="Application name for API key (e.g Web3 project)" error={ errors.name }/> <InputPlaceholder text="Application name for API key (e.g Web3 project)" error={ errors.name }/>
</FormControl> </FormControl>
); );
}, [ errors, formBackgroundColor ]); }, [ errors ]);
return ( return (
<form noValidate onSubmit={ handleSubmit(onSubmit) }> <form noValidate onSubmit={ handleSubmit(onSubmit) }>
......
...@@ -108,7 +108,6 @@ const BlobData = ({ data, isLoading, hash }: Props) => { ...@@ -108,7 +108,6 @@ const BlobData = ({ data, isLoading, hash }: Props) => {
borderRadius="base" borderRadius="base"
value={ format } value={ format }
onChange={ handleSelectChange } onChange={ handleSelectChange }
focusBorderColor="none"
w="auto" w="auto"
> >
{ formats.map((format) => ( { formats.map((format) => (
......
...@@ -24,7 +24,7 @@ const BlockWithdrawals = ({ blockWithdrawalsQuery }: Props) => { ...@@ -24,7 +24,7 @@ const BlockWithdrawals = ({ blockWithdrawalsQuery }: Props) => {
<BeaconChainWithdrawalsTable <BeaconChainWithdrawalsTable
items={ blockWithdrawalsQuery.data.items } items={ blockWithdrawalsQuery.data.items }
isLoading={ blockWithdrawalsQuery.isPlaceholderData } isLoading={ blockWithdrawalsQuery.isPlaceholderData }
top={ blockWithdrawalsQuery.pagination.isVisible ? 80 : 0 } top={ blockWithdrawalsQuery.pagination.isVisible ? 76 : 0 }
view="block" view="block"
/> />
</Hide> </Hide>
......
...@@ -95,7 +95,7 @@ const BlocksContent = ({ type, query }: Props) => { ...@@ -95,7 +95,7 @@ const BlocksContent = ({ type, query }: Props) => {
<Box display={{ base: 'none', lg: 'block' }}> <Box display={{ base: 'none', lg: 'block' }}>
<BlocksTable <BlocksTable
data={ query.data.items } data={ query.data.items }
top={ query.pagination.isVisible ? 80 : 0 } top={ query.pagination.isVisible ? 76 : 0 }
page={ query.pagination.page } page={ query.pagination.page }
isLoading={ query.isPlaceholderData } isLoading={ query.isPlaceholderData }
showSocketInfo={ query.pagination.page === 1 } showSocketInfo={ query.pagination.page === 1 }
......
...@@ -4,7 +4,6 @@ import { ...@@ -4,7 +4,6 @@ import {
FormControl, FormControl,
Input, Input,
Textarea, Textarea,
useColorModeValue,
} from '@chakra-ui/react'; } from '@chakra-ui/react';
import { useMutation, useQueryClient } from '@tanstack/react-query'; import { useMutation, useQueryClient } from '@tanstack/react-query';
import React, { useCallback } from 'react'; import React, { useCallback } from 'react';
...@@ -61,8 +60,6 @@ const CustomAbiForm: React.FC<Props> = ({ data, onClose, setAlertVisible }) => { ...@@ -61,8 +60,6 @@ const CustomAbiForm: React.FC<Props> = ({ data, onClose, setAlertVisible }) => {
}); });
}; };
const formBackgroundColor = useColorModeValue('white', 'gray.900');
const mutation = useMutation({ const mutation = useMutation({
mutationFn: customAbiKey, mutationFn: customAbiKey,
onSuccess: (data) => { onSuccess: (data) => {
...@@ -109,38 +106,40 @@ const CustomAbiForm: React.FC<Props> = ({ data, onClose, setAlertVisible }) => { ...@@ -109,38 +106,40 @@ const CustomAbiForm: React.FC<Props> = ({ data, onClose, setAlertVisible }) => {
<AddressInput<Inputs, 'contract_address_hash'> <AddressInput<Inputs, 'contract_address_hash'>
field={ field } field={ field }
error={ errors.contract_address_hash } error={ errors.contract_address_hash }
backgroundColor={ formBackgroundColor } bgColor="dialog_bg"
placeholder="Smart contract address (0x...)" placeholder="Smart contract address (0x...)"
/> />
); );
}, [ errors, formBackgroundColor ]); }, [ errors ]);
const renderNameInput = useCallback(({ field }: {field: ControllerRenderProps<Inputs, 'name'>}) => { const renderNameInput = useCallback(({ field }: {field: ControllerRenderProps<Inputs, 'name'>}) => {
return ( return (
<FormControl variant="floating" id="name" isRequired backgroundColor={ formBackgroundColor }> <FormControl variant="floating" id="name" isRequired bgColor="dialog_bg">
<Input <Input
{ ...field } { ...field }
isInvalid={ Boolean(errors.name) } isInvalid={ Boolean(errors.name) }
maxLength={ NAME_MAX_LENGTH } maxLength={ NAME_MAX_LENGTH }
bgColor="dialog_bg"
/> />
<InputPlaceholder text="Project name" error={ errors.name }/> <InputPlaceholder text="Project name" error={ errors.name }/>
</FormControl> </FormControl>
); );
}, [ errors, formBackgroundColor ]); }, [ errors ]);
const renderAbiInput = useCallback(({ field }: {field: ControllerRenderProps<Inputs, 'abi'>}) => { const renderAbiInput = useCallback(({ field }: {field: ControllerRenderProps<Inputs, 'abi'>}) => {
return ( return (
<FormControl variant="floating" id="abi" isRequired backgroundColor={ formBackgroundColor }> <FormControl variant="floating" id="abi" isRequired bgColor="dialog_bg">
<Textarea <Textarea
{ ...field } { ...field }
size="lg" size="lg"
minH="300px" minH="300px"
isInvalid={ Boolean(errors.abi) } isInvalid={ Boolean(errors.abi) }
bgColor="dialog_bg"
/> />
<InputPlaceholder text="Custom ABI [{...}] (JSON format)" error={ errors.abi }/> <InputPlaceholder text="Custom ABI [{...}] (JSON format)" error={ errors.abi }/>
</FormControl> </FormControl>
); );
}, [ errors, formBackgroundColor ]); }, [ errors ]);
return ( return (
<form noValidate onSubmit={ handleSubmit(onSubmit) }> <form noValidate onSubmit={ handleSubmit(onSubmit) }>
......
...@@ -77,17 +77,7 @@ const LatestBlocks = () => { ...@@ -77,17 +77,7 @@ const LatestBlocks = () => {
content = ( content = (
<> <>
{ statsQueryResult.data?.network_utilization_percentage !== undefined && ( <VStack spacing={ 2 } mb={ 3 } overflow="hidden" alignItems="stretch">
<Skeleton isLoaded={ !statsQueryResult.isPlaceholderData } mb={{ base: 6, lg: 3 }} display="inline-block">
<Text as="span" fontSize="sm">
Network utilization:{ nbsp }
</Text>
<Text as="span" fontSize="sm" color="blue.400" fontWeight={ 700 }>
{ statsQueryResult.data?.network_utilization_percentage.toFixed(2) }%
</Text>
</Skeleton>
) }
<VStack spacing={ 3 } mb={ 4 } overflow="hidden" alignItems="stretch">
<AnimatePresence initial={ false } > <AnimatePresence initial={ false } >
{ dataToShow.map(((block, index) => ( { dataToShow.map(((block, index) => (
<LatestBlocksItem <LatestBlocksItem
...@@ -107,8 +97,20 @@ const LatestBlocks = () => { ...@@ -107,8 +97,20 @@ const LatestBlocks = () => {
return ( return (
<Box width={{ base: '100%', lg: '280px' }} flexShrink={ 0 }> <Box width={{ base: '100%', lg: '280px' }} flexShrink={ 0 }>
<Heading as="h4" size="sm" mb={ 4 }>Latest blocks</Heading> <Heading as="h4" size="sm">Latest blocks</Heading>
{ content } { statsQueryResult.data?.network_utilization_percentage !== undefined && (
<Skeleton isLoaded={ !statsQueryResult.isPlaceholderData } mt={ 1 } display="inline-block">
<Text as="span" fontSize="sm">
Network utilization:{ nbsp }
</Text>
<Text as="span" fontSize="sm" color="blue.400" fontWeight={ 700 }>
{ statsQueryResult.data?.network_utilization_percentage.toFixed(2) }%
</Text>
</Skeleton>
) }
<Box mt={ 3 }>
{ content }
</Box>
</Box> </Box>
); );
}; };
......
...@@ -34,7 +34,7 @@ const LatestBlocksItem = ({ block, isLoading }: Props) => { ...@@ -34,7 +34,7 @@ const LatestBlocksItem = ({ block, isLoading }: Props) => {
borderRadius="md" borderRadius="md"
border="1px solid" border="1px solid"
borderColor="divider" borderColor="divider"
p={ 6 } p={ 3 }
> >
<Flex alignItems="center" overflow="hidden" w="100%" mb={ 3 }> <Flex alignItems="center" overflow="hidden" w="100%" mb={ 3 }>
<BlockEntity <BlockEntity
......
...@@ -44,7 +44,7 @@ const LatestTransactions = () => { ...@@ -44,7 +44,7 @@ const LatestTransactions = () => {
))) } ))) }
</Box> </Box>
<AddressHighlightProvider> <AddressHighlightProvider>
<Box mb={ 4 } display={{ base: 'none', lg: 'block' }}> <Box mb={ 3 } display={{ base: 'none', lg: 'block' }}>
{ data.slice(0, txsCount).map(((tx, index) => ( { data.slice(0, txsCount).map(((tx, index) => (
<LatestTxsItem <LatestTxsItem
key={ tx.hash + (isPlaceholderData ? index : '') } key={ tx.hash + (isPlaceholderData ? index : '') }
......
...@@ -61,7 +61,7 @@ const LatestZkEvmL2Batches = () => { ...@@ -61,7 +61,7 @@ const LatestZkEvmL2Batches = () => {
content = ( content = (
<> <>
<VStack spacing={ 3 } mb={ 4 } overflow="hidden" alignItems="stretch"> <VStack spacing={ 2 } mb={ 3 } overflow="hidden" alignItems="stretch">
<AnimatePresence initial={ false } > <AnimatePresence initial={ false } >
{ dataToShow.map(((batch, index) => ( { dataToShow.map(((batch, index) => (
<LatestZkevmL2BatchItem <LatestZkevmL2BatchItem
...@@ -81,7 +81,7 @@ const LatestZkEvmL2Batches = () => { ...@@ -81,7 +81,7 @@ const LatestZkEvmL2Batches = () => {
return ( return (
<Box width={{ base: '100%', lg: '280px' }} flexShrink={ 0 }> <Box width={{ base: '100%', lg: '280px' }} flexShrink={ 0 }>
<Heading as="h4" size="sm" mb={ 4 }>Latest batches</Heading> <Heading as="h4" size="sm" mb={ 3 }>Latest batches</Heading>
{ content } { content }
</Box> </Box>
); );
......
...@@ -32,7 +32,7 @@ const LatestZkevmL2BatchItem = ({ batch, isLoading }: Props) => { ...@@ -32,7 +32,7 @@ const LatestZkevmL2BatchItem = ({ batch, isLoading }: Props) => {
borderRadius="md" borderRadius="md"
border="1px solid" border="1px solid"
borderColor="divider" borderColor="divider"
p={ 6 } p={ 3 }
> >
<Flex alignItems="center" overflow="hidden" w="100%" mb={ 3 }> <Flex alignItems="center" overflow="hidden" w="100%" mb={ 3 }>
<BatchEntityL2 <BatchEntityL2
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment