Merge pull request #2690 from blockscout/release/v2-0-0
Fixes for release `v2.0.0`
@@ -15,7 +15,9 @@ on:
|
||||
description: Image platforms (you can specify multiple platforms separated by comma)
|
||||
required: false
|
||||
type: string
|
||||
default: linux/amd64
|
||||
default: |
|
||||
linux/amd64
|
||||
linux/arm64/v8
|
||||
workflow_call:
|
||||
inputs:
|
||||
tags:
|
||||
@@ -30,7 +32,9 @@ on:
|
||||
description: Image platforms (you can specify multiple platforms separated by comma)
|
||||
required: false
|
||||
type: string
|
||||
default: linux/amd64
|
||||
default: |
|
||||
linux/amd64
|
||||
linux/arm64/v8
|
||||
|
||||
jobs:
|
||||
run:
|
||||
@@ -40,9 +44,6 @@ jobs:
|
||||
- name: Check out the repo
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
# Will automatically make nice tags, see the table here https://github.com/docker/metadata-action#basic
|
||||
- name: Docker meta
|
||||
id: meta
|
||||
@@ -55,13 +56,6 @@ jobs:
|
||||
type=ref,event=tag
|
||||
${{ inputs.tags }}
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Add SHORT_SHA env property with commit short sha
|
||||
run: echo "SHORT_SHA=`echo ${GITHUB_SHA} | cut -c1-8`" >> $GITHUB_ENV
|
||||
|
||||
@@ -73,6 +67,17 @@ jobs:
|
||||
echo "ref_type: $REF_TYPE"
|
||||
echo "ref_name: $REF_NAME"
|
||||
|
||||
- name: Setup repo
|
||||
uses: blockscout/actions/.github/actions/setup-multiarch-buildx@no-metadata
|
||||
id: setup
|
||||
with:
|
||||
docker-image: ghcr.io/blockscout/frontend
|
||||
docker-username: ${{ github.actor }}
|
||||
docker-password: ${{ secrets.GITHUB_TOKEN }}
|
||||
docker-remote-multi-platform: true
|
||||
docker-arm-host: ${{ secrets.ARM_RUNNER_HOSTNAME }}
|
||||
docker-arm-host-key: ${{ secrets.ARM_RUNNER_KEY }}
|
||||
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
|
||||
@@ -59,7 +59,7 @@ module.exports = {
|
||||
{
|
||||
userAgent: '*',
|
||||
allow: '/',
|
||||
disallow: ['/auth/*', '/login', '/chakra', '/sprite', '/account/*', '/api/*', '/node-api/*'],
|
||||
disallow: ['/auth/*', '/login', '/chakra', '/sprite', '/account/*'],
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
<svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M12.78 7.28a.75.75 0 0 0-1.06-1.06l-6.97 6.97-3.47-3.47a.75.75 0 0 0-1.06 1.06l4 4a.75.75 0 0 0 1.06 0l7.5-7.5Z" fill="currentColor"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 226 B |
@@ -1,6 +1,6 @@
|
||||
import { useCallback, useRef, useEffect } from 'react';
|
||||
|
||||
import type { PreSubmitTransactionResponse, PreVerifyContractResponse } from '@blockscout/points-types';
|
||||
import type { PreSubmitTransactionResponse } from '@blockscout/points-types';
|
||||
|
||||
import config from 'configs/app';
|
||||
import useApiFetch from 'lib/api/useApiFetch';
|
||||
@@ -74,17 +74,11 @@ export default function useRewardsActivity() {
|
||||
[ makeRequest ],
|
||||
);
|
||||
|
||||
const trackContract = useCallback(async(address: string) => {
|
||||
return (
|
||||
await makeRequest('rewards_user_activity_track_contract', {
|
||||
address,
|
||||
chain_id: config.chain.id ?? '',
|
||||
})
|
||||
) as PreVerifyContractResponse | undefined;
|
||||
}, [ makeRequest ]);
|
||||
|
||||
const trackContractConfirm = useCallback((token: string) =>
|
||||
makeRequest('rewards_user_activity_track_contract_confirm', { token }),
|
||||
const trackContract = useCallback(async(address: string) =>
|
||||
makeRequest('rewards_user_activity_track_contract', {
|
||||
address,
|
||||
chain_id: config.chain.id ?? '',
|
||||
}),
|
||||
[ makeRequest ],
|
||||
);
|
||||
|
||||
@@ -115,7 +109,6 @@ export default function useRewardsActivity() {
|
||||
trackTransaction,
|
||||
trackTransactionConfirm,
|
||||
trackContract,
|
||||
trackContractConfirm,
|
||||
trackUsage,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import useApiQuery from 'lib/api/useApiQuery';
|
||||
import useAccount from './useAccount';
|
||||
|
||||
export default function useAccountWithDomain(isEnabled: boolean) {
|
||||
const { address } = useAccount();
|
||||
const { address, isConnecting } = useAccount();
|
||||
|
||||
const isQueryEnabled = config.features.nameService.isEnabled && Boolean(address) && Boolean(isEnabled);
|
||||
|
||||
@@ -25,7 +25,7 @@ export default function useAccountWithDomain(isEnabled: boolean) {
|
||||
return {
|
||||
address: isEnabled ? address : undefined,
|
||||
domain: domainQuery.data?.domain?.name,
|
||||
isLoading: isQueryEnabled && domainQuery.isLoading,
|
||||
isLoading: (isQueryEnabled && domainQuery.isLoading) || isConnecting,
|
||||
};
|
||||
}, [ address, domainQuery.data?.domain?.name, domainQuery.isLoading, isEnabled, isQueryEnabled ]);
|
||||
}, [ address, domainQuery.data?.domain?.name, domainQuery.isLoading, isEnabled, isQueryEnabled, isConnecting ]);
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@blockscout/bens-types": "1.4.1",
|
||||
"@blockscout/points-types": "1.3.0-alpha.1",
|
||||
"@blockscout/points-types": "1.3.0-alpha.2",
|
||||
"@blockscout/stats-types": "2.5.0-alpha",
|
||||
"@blockscout/visualizer-types": "0.2.0",
|
||||
"@chakra-ui/react": "3.15.0",
|
||||
|
||||
@@ -44,6 +44,7 @@
|
||||
| "contracts/regular"
|
||||
| "contracts/verified_many"
|
||||
| "contracts/verified"
|
||||
| "copy_check"
|
||||
| "copy"
|
||||
| "cross"
|
||||
| "delete"
|
||||
|
||||
@@ -43,7 +43,7 @@ export const Image = React.forwardRef<HTMLImageElement, ImageProps>(
|
||||
onError={ handleLoadError }
|
||||
onLoad={ handleLoadSuccess }
|
||||
{ ...rest }
|
||||
display={ loading ? 'none' : rest.display || 'inline-block' }
|
||||
display={ loading ? 'none' : rest.display || 'block' }
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -234,10 +234,11 @@ export interface SelectProps extends SelectRootProps {
|
||||
portalled?: boolean;
|
||||
loading?: boolean;
|
||||
errorText?: string;
|
||||
contentProps?: SelectContentProps;
|
||||
}
|
||||
|
||||
export const Select = React.forwardRef<HTMLDivElement, SelectProps>((props, ref) => {
|
||||
const { collection, placeholder, portalled = true, loading, errorText, ...rest } = props;
|
||||
const { collection, placeholder, portalled = true, loading, errorText, contentProps, ...rest } = props;
|
||||
return (
|
||||
<SelectRoot
|
||||
ref={ ref }
|
||||
@@ -253,7 +254,7 @@ export const Select = React.forwardRef<HTMLDivElement, SelectProps>((props, ref)
|
||||
errorText={ errorText }
|
||||
/>
|
||||
</SelectControl>
|
||||
<SelectContent portalled={ portalled }>
|
||||
<SelectContent portalled={ portalled } { ...contentProps }>
|
||||
{ collection.items.map((item: SelectOption) => (
|
||||
<SelectItem item={ item } key={ item.value }>
|
||||
{ item.label }
|
||||
|
||||
@@ -35,13 +35,30 @@ export const Tooltip = React.forwardRef<HTMLDivElement, TooltipProps>(
|
||||
lazyMount = true,
|
||||
unmountOnExit = true,
|
||||
triggerProps,
|
||||
closeDelay = 100,
|
||||
openDelay = 100,
|
||||
interactive,
|
||||
...rest
|
||||
} = props;
|
||||
|
||||
const [ open, setOpen ] = React.useState<boolean>(defaultOpen);
|
||||
const timeoutRef = React.useRef<number | null>(null);
|
||||
|
||||
const isMobile = useIsMobile();
|
||||
const triggerRef = useClickAway<HTMLButtonElement>(() => setOpen(false));
|
||||
|
||||
const handleClickAway = React.useCallback((event: Event) => {
|
||||
if (interactive) {
|
||||
const closest = (event.target as HTMLElement)?.closest('.chakra-tooltip__positioner');
|
||||
if (closest) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
timeoutRef.current = window.setTimeout(() => {
|
||||
setOpen(false);
|
||||
}, closeDelay);
|
||||
}, [ closeDelay, interactive ]);
|
||||
const triggerRef = useClickAway<HTMLButtonElement>(handleClickAway);
|
||||
|
||||
const handleOpenChange = React.useCallback((details: { open: boolean }) => {
|
||||
setOpen(details.open);
|
||||
@@ -49,8 +66,25 @@ export const Tooltip = React.forwardRef<HTMLDivElement, TooltipProps>(
|
||||
}, [ onOpenChange ]);
|
||||
|
||||
const handleTriggerClick = React.useCallback(() => {
|
||||
setOpen((prev) => !prev);
|
||||
}, [ ]);
|
||||
if (timeoutRef.current) {
|
||||
window.clearTimeout(timeoutRef.current);
|
||||
}
|
||||
timeoutRef.current = window.setTimeout(() => {
|
||||
setOpen((prev) => !prev);
|
||||
}, open ? closeDelay : openDelay);
|
||||
}, [ open, openDelay, closeDelay ]);
|
||||
|
||||
const handleContentClick = React.useCallback((event: React.MouseEvent<HTMLDivElement>) => {
|
||||
event.stopPropagation();
|
||||
}, []);
|
||||
|
||||
React.useEffect(() => {
|
||||
return () => {
|
||||
if (timeoutRef.current) {
|
||||
clearTimeout(timeoutRef.current);
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
|
||||
if (disabled || (disableOnMobile && isMobile)) return children;
|
||||
|
||||
@@ -68,22 +102,23 @@ export const Tooltip = React.forwardRef<HTMLDivElement, TooltipProps>(
|
||||
|
||||
return (
|
||||
<ChakraTooltip.Root
|
||||
openDelay={ 100 }
|
||||
openDelay={ openDelay }
|
||||
// FIXME: chakra closes tooltip too fast, so Playwright is not able to make a screenshot of its content
|
||||
// so we need to increase the close delay in Playwright environment
|
||||
closeDelay={ config.app.isPw ? 10_000 : 100 }
|
||||
closeDelay={ config.app.isPw ? 10_000 : closeDelay }
|
||||
open={ open }
|
||||
onOpenChange={ handleOpenChange }
|
||||
closeOnClick={ false }
|
||||
closeOnPointerDown={ true }
|
||||
closeOnPointerDown={ false }
|
||||
variant={ variant }
|
||||
lazyMount={ lazyMount }
|
||||
unmountOnExit={ unmountOnExit }
|
||||
interactive={ interactive }
|
||||
{ ...rest }
|
||||
positioning={ positioning }
|
||||
>
|
||||
<ChakraTooltip.Trigger
|
||||
ref={ triggerRef }
|
||||
ref={ open ? triggerRef : null }
|
||||
asChild
|
||||
onClick={ isMobile ? handleTriggerClick : undefined }
|
||||
{ ...triggerProps }
|
||||
@@ -94,6 +129,7 @@ export const Tooltip = React.forwardRef<HTMLDivElement, TooltipProps>(
|
||||
<ChakraTooltip.Positioner>
|
||||
<ChakraTooltip.Content
|
||||
ref={ ref }
|
||||
onClick={ interactive ? handleContentClick : undefined }
|
||||
{ ...(selected ? { 'data-selected': true } : {}) }
|
||||
{ ...contentProps }
|
||||
>
|
||||
|
||||
@@ -71,6 +71,7 @@ const AdaptiveTabs = (props: Props) => {
|
||||
stickyEnabled={ stickyEnabled }
|
||||
activeTab={ activeTab }
|
||||
isLoading={ isLoading }
|
||||
variant={ variant }
|
||||
/>
|
||||
{ tabs.map((tab) => {
|
||||
const value = getTabValue(tab);
|
||||
|
||||
@@ -9,6 +9,7 @@ import useIsMobile from 'lib/hooks/useIsMobile';
|
||||
|
||||
import { useIsSticky } from '../..//hooks/useIsSticky';
|
||||
import { Skeleton } from '../../chakra/skeleton';
|
||||
import type { TabsProps } from '../../chakra/tabs';
|
||||
import { TabsCounter, TabsList, TabsTrigger } from '../../chakra/tabs';
|
||||
import AdaptiveTabsMenu from './AdaptiveTabsMenu';
|
||||
import useAdaptiveTabs from './useAdaptiveTabs';
|
||||
@@ -32,6 +33,7 @@ export interface BaseProps {
|
||||
|
||||
interface Props extends BaseProps {
|
||||
activeTab: string;
|
||||
variant: TabsProps['variant'];
|
||||
}
|
||||
|
||||
const HIDDEN_ITEM_STYLES: HTMLChakraProps<'button'> = {
|
||||
@@ -41,19 +43,17 @@ const HIDDEN_ITEM_STYLES: HTMLChakraProps<'button'> = {
|
||||
visibility: 'hidden',
|
||||
};
|
||||
|
||||
const getItemStyles = (index: number, tabsCut: number | undefined) => {
|
||||
if (tabsCut === undefined) {
|
||||
const getItemStyles = (index: number, tabsCut: number | undefined, isLoading: boolean | undefined) => {
|
||||
if (tabsCut === undefined || isLoading) {
|
||||
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,
|
||||
};
|
||||
const getMenuStyles = (tabsLength: number, tabsCut: number | undefined, isLoading: boolean | undefined) => {
|
||||
if (tabsCut === undefined || isLoading) {
|
||||
return HIDDEN_ITEM_STYLES;
|
||||
}
|
||||
|
||||
return tabsCut >= tabsLength ? HIDDEN_ITEM_STYLES : {};
|
||||
@@ -71,6 +71,7 @@ const AdaptiveTabsList = (props: Props) => {
|
||||
leftSlotProps,
|
||||
stickyEnabled,
|
||||
isLoading,
|
||||
variant,
|
||||
} = props;
|
||||
|
||||
const scrollDirection = useScrollDirection();
|
||||
@@ -80,11 +81,13 @@ const AdaptiveTabsList = (props: Props) => {
|
||||
return [ ...tabs, menuButton ];
|
||||
}, [ tabs ]);
|
||||
|
||||
const { tabsCut, tabsRefs, listRef, rightSlotRef, leftSlotRef } = useAdaptiveTabs(tabsList, isMobile);
|
||||
const { tabsCut, tabsRefs, listRef, rightSlotRef, leftSlotRef } = useAdaptiveTabs(tabsList, isLoading || isMobile);
|
||||
const isSticky = useIsSticky(listRef, 5, stickyEnabled);
|
||||
const activeTabIndex = tabsList.findIndex((tab) => getTabValue(tab) === activeTab) ?? 0;
|
||||
useScrollToActiveTab({ activeTabIndex, listRef, tabsRefs, isMobile, isLoading });
|
||||
|
||||
const isReady = !isLoading && tabsCut !== undefined;
|
||||
|
||||
return (
|
||||
<TabsList
|
||||
ref={ listRef }
|
||||
@@ -92,10 +95,6 @@ const AdaptiveTabsList = (props: Props) => {
|
||||
alignItems="center"
|
||||
whiteSpace="nowrap"
|
||||
bgColor={{ _light: 'white', _dark: 'black' }}
|
||||
// initially our cut is 0 and we don't want to show the list
|
||||
// but we want to keep all items in the tabs row so it won't collapse
|
||||
// that's why we only change opacity but not the position itself
|
||||
opacity={ tabsCut ? 1 : 0 }
|
||||
marginBottom={ 6 }
|
||||
mx={{ base: '-12px', lg: 'unset' }}
|
||||
px={{ base: '12px', lg: 'unset' }}
|
||||
@@ -134,15 +133,11 @@ const AdaptiveTabsList = (props: Props) => {
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
{ tabsList.slice(0, isLoading ? 5 : Infinity).map((tab, index) => {
|
||||
{ tabsList.map((tab, index) => {
|
||||
const value = getTabValue(tab);
|
||||
const ref = tabsRefs[index];
|
||||
|
||||
if (tab.id === 'menu') {
|
||||
if (isLoading) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<AdaptiveTabsMenu
|
||||
key="menu"
|
||||
@@ -150,7 +145,7 @@ const AdaptiveTabsList = (props: Props) => {
|
||||
tabs={ tabs }
|
||||
tabsCut={ tabsCut ?? 0 }
|
||||
isActive={ activeTabIndex > 0 && tabsCut !== undefined && tabsCut > 0 && activeTabIndex >= tabsCut }
|
||||
{ ...getMenuStyles(tabs.length, tabsCut) }
|
||||
{ ...getMenuStyles(tabs.length, tabsCut, isLoading) }
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -162,19 +157,30 @@ const AdaptiveTabsList = (props: Props) => {
|
||||
ref={ ref }
|
||||
scrollSnapAlign="start"
|
||||
flexShrink={ 0 }
|
||||
{ ...getItemStyles(index, tabsCut) }
|
||||
{ ...getItemStyles(index, tabsCut, isLoading) }
|
||||
>
|
||||
{ isLoading ? (
|
||||
<Skeleton loading>
|
||||
{ typeof tab.title === 'function' ? tab.title() : tab.title }
|
||||
<TabsCounter count={ tab.count }/>
|
||||
</Skeleton>
|
||||
) : (
|
||||
<>
|
||||
{ typeof tab.title === 'function' ? tab.title() : tab.title }
|
||||
<TabsCounter count={ tab.count }/>
|
||||
</>
|
||||
) }
|
||||
{ typeof tab.title === 'function' ? tab.title() : tab.title }
|
||||
<TabsCounter count={ tab.count }/>
|
||||
</TabsTrigger>
|
||||
);
|
||||
}) }
|
||||
{ tabs.slice(0, isReady ? 0 : 5).map((tab, index) => {
|
||||
const value = `${ getTabValue(tab) }-pre`;
|
||||
return (
|
||||
<TabsTrigger
|
||||
key={ value }
|
||||
value={ value }
|
||||
flexShrink={ 0 }
|
||||
bgColor={
|
||||
activeTabIndex === index && (variant === 'solid' || variant === undefined) ?
|
||||
{ _light: 'blackAlpha.50', _dark: 'whiteAlpha.50' } :
|
||||
undefined
|
||||
}
|
||||
>
|
||||
<Skeleton loading>
|
||||
{ typeof tab.title === 'function' ? tab.title() : tab.title }
|
||||
<TabsCounter count={ tab.count }/>
|
||||
</Skeleton>
|
||||
</TabsTrigger>
|
||||
);
|
||||
}) }
|
||||
|
||||
@@ -1,73 +0,0 @@
|
||||
import { Flex, chakra, Box } from '@chakra-ui/react';
|
||||
import React from 'react';
|
||||
|
||||
import type { TabItemRegular } from '../AdaptiveTabs/types';
|
||||
|
||||
import { Skeleton } from '../../chakra/skeleton';
|
||||
import type { TabsProps } from '../../chakra/tabs';
|
||||
import useActiveTabFromQuery from './useActiveTabFromQuery';
|
||||
|
||||
const SkeletonTabText = ({ size, title }: { size: TabsProps['size']; title: TabItemRegular['title'] }) => (
|
||||
<Skeleton
|
||||
borderRadius="base"
|
||||
borderWidth={ size === 'sm' ? '2px' : 0 }
|
||||
fontWeight={ 600 }
|
||||
mx={ size === 'sm' ? 3 : 4 }
|
||||
flexShrink={ 0 }
|
||||
loading
|
||||
>
|
||||
{ typeof title === 'string' ? title : title() }
|
||||
</Skeleton>
|
||||
);
|
||||
|
||||
interface Props {
|
||||
className?: string;
|
||||
tabs: Array<TabItemRegular>;
|
||||
size?: 'sm' | 'md';
|
||||
}
|
||||
|
||||
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 (
|
||||
<Flex className={ className } my={ 8 } alignItems="center" overflow="hidden">
|
||||
{ tabs.slice(0, tabIndex).map(({ title, id }) => (
|
||||
<SkeletonTabText
|
||||
key={ id.toString() }
|
||||
title={ title }
|
||||
size={ size }
|
||||
/>
|
||||
)) }
|
||||
{ tabs.slice(tabIndex, tabIndex + 1).map(({ title, id }) => (
|
||||
<Box
|
||||
key={ id.toString() }
|
||||
bgColor={{ _light: 'blackAlpha.50', _dark: 'whiteAlpha.50' }}
|
||||
py={ size === 'sm' ? 1 : 2 }
|
||||
borderRadius="base"
|
||||
flexShrink={ 0 }
|
||||
>
|
||||
<SkeletonTabText
|
||||
key={ id.toString() }
|
||||
title={ title }
|
||||
size={ size }
|
||||
/>
|
||||
</Box>
|
||||
)) }
|
||||
{ tabs.slice(tabIndex + 1).map(({ title, id }) => (
|
||||
<SkeletonTabText
|
||||
key={ id.toString() }
|
||||
title={ title }
|
||||
size={ size }
|
||||
/>
|
||||
)) }
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
export default chakra(RoutedTabsSkeleton);
|
||||
@@ -1,3 +1,2 @@
|
||||
export { default as RoutedTabs } from './RoutedTabs';
|
||||
export { default as RoutedTabsSkeleton } from './RoutedTabsSkeleton';
|
||||
export { default as useActiveTabFromQuery } from './useActiveTabFromQuery';
|
||||
|
||||
@@ -11,9 +11,10 @@ export interface TruncatedTextTooltipProps {
|
||||
children: React.ReactNode;
|
||||
label: React.ReactNode;
|
||||
placement?: Placement;
|
||||
interactive?: boolean;
|
||||
}
|
||||
|
||||
export const TruncatedTextTooltip = React.memo(({ children, label, placement }: TruncatedTextTooltipProps) => {
|
||||
export const TruncatedTextTooltip = React.memo(({ children, label, placement, interactive }: TruncatedTextTooltipProps) => {
|
||||
const childRef = React.useRef<HTMLElement>(null);
|
||||
const [ isTruncated, setTruncated ] = React.useState(false);
|
||||
const { open, onToggle, onOpen, onClose } = useDisclosure();
|
||||
@@ -83,6 +84,7 @@ export const TruncatedTextTooltip = React.memo(({ children, label, placement }:
|
||||
contentProps={{ maxW: { base: 'calc(100vw - 8px)', lg: '400px' } }}
|
||||
positioning={{ placement }}
|
||||
open={ open }
|
||||
interactive={ interactive }
|
||||
>
|
||||
{ modifiedChildren }
|
||||
</Tooltip>
|
||||
|
||||
@@ -3,17 +3,16 @@ export const zIndex = {
|
||||
auto: { value: 'auto' },
|
||||
base: { value: 0 },
|
||||
docked: { value: 10 },
|
||||
dropdown: { value: 1000 },
|
||||
sticky: { value: 1100 },
|
||||
sticky1: { value: 1101 },
|
||||
sticky2: { value: 1102 },
|
||||
popover: { value: 1150 },
|
||||
banner: { value: 1200 },
|
||||
overlay: { value: 1300 },
|
||||
modal: { value: 1400 },
|
||||
popover: { value: 1500 },
|
||||
modal2: { value: 14001 },
|
||||
tooltip: { value: 1550 }, // otherwise tooltips will not be visible in modals
|
||||
tooltip2: { value: 1551 }, // for tooltips in tooltips
|
||||
skipLink: { value: 1600 },
|
||||
toast: { value: 1700 },
|
||||
};
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ export const recipe = defineRecipe({
|
||||
bg: 'blue.600',
|
||||
color: 'white',
|
||||
_hover: {
|
||||
bg: 'blue.400',
|
||||
bg: 'link.primary.hover',
|
||||
},
|
||||
_loading: {
|
||||
opacity: 1,
|
||||
@@ -27,7 +27,7 @@ export const recipe = defineRecipe({
|
||||
},
|
||||
},
|
||||
_expanded: {
|
||||
bg: 'blue.400',
|
||||
bg: 'link.primary.hover',
|
||||
},
|
||||
},
|
||||
outline: {
|
||||
@@ -38,8 +38,8 @@ export const recipe = defineRecipe({
|
||||
borderColor: 'button.outline.fg',
|
||||
_hover: {
|
||||
bg: 'transparent',
|
||||
color: 'blue.400',
|
||||
borderColor: 'blue.400',
|
||||
color: 'link.primary.hover',
|
||||
borderColor: 'link.primary.hover',
|
||||
},
|
||||
_loading: {
|
||||
opacity: 1,
|
||||
@@ -58,8 +58,8 @@ export const recipe = defineRecipe({
|
||||
borderColor: 'button.dropdown.border',
|
||||
_hover: {
|
||||
bg: 'transparent',
|
||||
color: 'blue.400',
|
||||
borderColor: 'blue.400',
|
||||
color: 'link.primary.hover',
|
||||
borderColor: 'link.primary.hover',
|
||||
},
|
||||
_loading: {
|
||||
opacity: 1,
|
||||
@@ -72,8 +72,8 @@ export const recipe = defineRecipe({
|
||||
// When the dropdown is open, the button should be active
|
||||
_expanded: {
|
||||
bg: 'transparent',
|
||||
color: 'blue.400',
|
||||
borderColor: 'blue.400',
|
||||
color: 'link.primary.hover',
|
||||
borderColor: 'link.primary.hover',
|
||||
},
|
||||
// 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.
|
||||
@@ -84,9 +84,12 @@ export const recipe = defineRecipe({
|
||||
borderColor: 'transparent',
|
||||
_hover: {
|
||||
bg: 'button.dropdown.bg.selected',
|
||||
color: 'button.dropdown.fg.selected',
|
||||
color: 'link.primary.hover',
|
||||
borderColor: 'transparent',
|
||||
},
|
||||
_expanded: {
|
||||
color: 'link.primary.hover',
|
||||
},
|
||||
},
|
||||
},
|
||||
header: {
|
||||
@@ -97,8 +100,8 @@ export const recipe = defineRecipe({
|
||||
borderStyle: 'solid',
|
||||
_hover: {
|
||||
bg: 'transparent',
|
||||
color: 'blue.400',
|
||||
borderColor: 'blue.400',
|
||||
color: 'link.primary.hover',
|
||||
borderColor: 'link.primary.hover',
|
||||
},
|
||||
_loading: {
|
||||
opacity: 1,
|
||||
@@ -115,16 +118,22 @@ export const recipe = defineRecipe({
|
||||
borderWidth: '0px',
|
||||
_hover: {
|
||||
bg: 'button.header.bg.selected',
|
||||
color: 'button.header.fg.selected',
|
||||
color: 'link.primary.hover',
|
||||
},
|
||||
_expanded: {
|
||||
color: 'link.primary.hover',
|
||||
},
|
||||
_highlighted: {
|
||||
bg: 'button.header.bg.highlighted',
|
||||
color: 'button.header.fg.highlighted',
|
||||
borderColor: 'transparent',
|
||||
borderWidth: '0px',
|
||||
_expanded: {
|
||||
color: 'link.primary.hover',
|
||||
},
|
||||
_hover: {
|
||||
bg: 'button.header.bg.highlighted',
|
||||
color: 'button.header.fg.highlighted',
|
||||
color: 'link.primary.hover',
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -149,7 +158,10 @@ export const recipe = defineRecipe({
|
||||
color: 'button.hero.fg.selected',
|
||||
_hover: {
|
||||
bg: 'button.hero.bg.selected',
|
||||
color: 'button.hero.fg.selected',
|
||||
color: 'link.primary.hover',
|
||||
},
|
||||
_expanded: {
|
||||
color: 'link.primary.hover',
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -230,7 +242,10 @@ export const recipe = defineRecipe({
|
||||
color: 'button.icon_secondary.fg.selected',
|
||||
_hover: {
|
||||
bg: 'button.icon_secondary.bg.selected',
|
||||
color: 'button.icon_secondary.fg.selected',
|
||||
color: 'link.primary.hover',
|
||||
},
|
||||
_expanded: {
|
||||
color: 'link.primary.hover',
|
||||
},
|
||||
},
|
||||
_expanded: {
|
||||
|
||||
@@ -207,7 +207,7 @@ export const recipe = defineSlotRecipe({
|
||||
defaultVariants: {
|
||||
size: 'md',
|
||||
scrollBehavior: 'inside',
|
||||
placement: 'center',
|
||||
placement: { base: 'top', lg: 'center' },
|
||||
motionPreset: 'scale',
|
||||
},
|
||||
});
|
||||
|
||||
@@ -9,7 +9,7 @@ export const recipe = defineSlotRecipe({
|
||||
boxShadow: 'popover',
|
||||
color: 'initial',
|
||||
maxHeight: 'var(--available-height)',
|
||||
'--menu-z-index': 'zIndex.dropdown',
|
||||
'--menu-z-index': 'zIndex.popover',
|
||||
zIndex: 'calc(var(--menu-z-index) + var(--layer-index, 0))',
|
||||
borderRadius: 'md',
|
||||
overflow: 'hidden',
|
||||
@@ -25,7 +25,7 @@ export const recipe = defineSlotRecipe({
|
||||
},
|
||||
item: {
|
||||
textDecoration: 'none',
|
||||
color: 'initial',
|
||||
color: 'text.primary',
|
||||
userSelect: 'none',
|
||||
borderRadius: 'none',
|
||||
width: '100%',
|
||||
|
||||
@@ -27,7 +27,7 @@ export type ScrollL2TxnBatch = {
|
||||
confirmation_transaction: ScrollL2TxnBatchConfirmationTransaction;
|
||||
start_block_number: number;
|
||||
end_block_number: number;
|
||||
transactions_count: number;
|
||||
transactions_count: number | null;
|
||||
data_availability: {
|
||||
batch_data_container: 'in_blob4844' | 'in_calldata';
|
||||
};
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { chakra } from '@chakra-ui/react';
|
||||
import { pickBy } from 'es-toolkit';
|
||||
import React from 'react';
|
||||
|
||||
import type { AddressFromToFilter } from 'types/api/address';
|
||||
@@ -26,11 +27,11 @@ const AddressAdvancedFilterLink = ({ isLoading, address, typeFilter, directionFi
|
||||
return null;
|
||||
}
|
||||
|
||||
const queryParams = {
|
||||
const queryParams = pickBy({
|
||||
to_address_hashes_to_include: !directionFilter || directionFilter === 'to' ? [ address ] : undefined,
|
||||
from_address_hashes_to_include: !directionFilter || directionFilter === 'from' ? [ address ] : undefined,
|
||||
transaction_types: typeFilter.length > 0 ? typeFilter : ADVANCED_FILTER_TYPES.filter((type) => type !== 'coin_transfer'),
|
||||
};
|
||||
}, (value) => value !== undefined);
|
||||
|
||||
return (
|
||||
<Link
|
||||
|
||||
@@ -85,36 +85,6 @@ test.describe('socket', () => {
|
||||
// test cases which use socket cannot run in parallel since the socket server always run on the same port
|
||||
test.describe.configure({ mode: 'serial' });
|
||||
|
||||
test('without overload', async({ render, mockApiResponse, page, createSocket }) => {
|
||||
await mockApiResponse(
|
||||
'address_txs',
|
||||
{ items: [ txMock.base ], next_page_params: DEFAULT_PAGINATION },
|
||||
{ pathParams: { hash: CURRENT_ADDRESS } },
|
||||
);
|
||||
|
||||
await render(
|
||||
<Box pt={{ base: '134px', lg: 6 }}>
|
||||
<AddressTxs/>
|
||||
</Box>,
|
||||
{ hooksConfig },
|
||||
{ withSocket: true },
|
||||
);
|
||||
|
||||
const socket = await createSocket();
|
||||
const channel = await socketServer.joinChannel(socket, `addresses:${ CURRENT_ADDRESS.toLowerCase() }`);
|
||||
|
||||
const itemsCount = await page.locator('tbody tr').count();
|
||||
expect(itemsCount).toBe(2);
|
||||
|
||||
socketServer.sendMessage(socket, channel, 'transaction', { transactions: [ txMock.base2, txMock.base4 ] });
|
||||
|
||||
const thirdRow = page.locator('tbody tr:nth-child(3)');
|
||||
await thirdRow.waitFor();
|
||||
|
||||
const itemsCountNew = await page.locator('tbody tr').count();
|
||||
expect(itemsCountNew).toBe(4);
|
||||
});
|
||||
|
||||
test('with update', async({ render, mockApiResponse, page, createSocket }) => {
|
||||
await mockApiResponse(
|
||||
'address_txs',
|
||||
@@ -136,13 +106,13 @@ test.describe('socket', () => {
|
||||
const itemsCount = await page.locator('tbody tr').count();
|
||||
expect(itemsCount).toBe(2);
|
||||
|
||||
socketServer.sendMessage(socket, channel, 'transaction', { transactions: [ txMock.base, txMock.base2 ] });
|
||||
socketServer.sendMessage(socket, channel, 'transaction', { transactions: [ txMock.base ] });
|
||||
|
||||
const thirdRow = page.locator('tbody tr:nth-child(3)');
|
||||
await thirdRow.waitFor();
|
||||
const secondRow = page.locator('tbody tr:nth-child(2)');
|
||||
await secondRow.waitFor();
|
||||
|
||||
const itemsCountNew = await page.locator('tbody tr').count();
|
||||
expect(itemsCountNew).toBe(3);
|
||||
expect(itemsCountNew).toBe(2);
|
||||
});
|
||||
|
||||
test('with overload', async({ render, mockApiResponse, page, createSocket }) => {
|
||||
@@ -154,7 +124,7 @@ test.describe('socket', () => {
|
||||
|
||||
await render(
|
||||
<Box pt={{ base: '134px', lg: 6 }}>
|
||||
<AddressTxs overloadCount={ 2 }/>
|
||||
<AddressTxs/>
|
||||
</Box>,
|
||||
{ hooksConfig },
|
||||
{ withSocket: true },
|
||||
@@ -205,10 +175,10 @@ test.describe('socket', () => {
|
||||
const itemsCount = await page.locator('tbody tr').count();
|
||||
expect(itemsCount).toBe(2);
|
||||
|
||||
socketServer.sendMessage(socket, channel, 'transaction', { transactions: [ txMock.base2, txMock.base4 ] });
|
||||
socketServer.sendMessage(socket, channel, 'transaction', { transactions: [ txMock.base2 ] });
|
||||
|
||||
const thirdRow = page.locator('tbody tr:nth-child(3)');
|
||||
await thirdRow.waitFor();
|
||||
const secondRow = page.locator('tbody tr:nth-child(2)');
|
||||
await secondRow.waitFor();
|
||||
|
||||
const itemsCountNew = await page.locator('tbody tr').count();
|
||||
expect(itemsCountNew).toBe(3);
|
||||
@@ -229,7 +199,7 @@ test.describe('socket', () => {
|
||||
|
||||
await render(
|
||||
<Box pt={{ base: '134px', lg: 6 }}>
|
||||
<AddressTxs overloadCount={ 2 }/>
|
||||
<AddressTxs/>
|
||||
</Box>,
|
||||
{ hooksConfig: hooksConfigWithFilter },
|
||||
{ withSocket: true },
|
||||
|
||||
@@ -1,19 +1,14 @@
|
||||
import { useQueryClient } from '@tanstack/react-query';
|
||||
import { useRouter } from 'next/router';
|
||||
import React from 'react';
|
||||
|
||||
import type { SocketMessage } from 'lib/socket/types';
|
||||
import type { AddressFromToFilter, AddressTransactionsResponse } from 'types/api/address';
|
||||
import type { AddressFromToFilter } from 'types/api/address';
|
||||
import { AddressFromToFilterValues } from 'types/api/address';
|
||||
import type { Transaction, TransactionsSortingField, TransactionsSortingValue, TransactionsSorting } from 'types/api/transaction';
|
||||
import type { TransactionsSortingField, TransactionsSortingValue, TransactionsSorting } from 'types/api/transaction';
|
||||
|
||||
import { getResourceKey } from 'lib/api/useApiQuery';
|
||||
import getFilterValueFromQuery from 'lib/getFilterValueFromQuery';
|
||||
import useIsMobile from 'lib/hooks/useIsMobile';
|
||||
import useIsMounted from 'lib/hooks/useIsMounted';
|
||||
import getQueryParamString from 'lib/router/getQueryParamString';
|
||||
import useSocketChannel from 'lib/socket/useSocketChannel';
|
||||
import useSocketMessage from 'lib/socket/useSocketMessage';
|
||||
import { TX } from 'stubs/tx';
|
||||
import { generateListStub } from 'stubs/utils';
|
||||
import ActionBar, { ACTION_BAR_HEIGHT_DESKTOP } from 'ui/shared/ActionBar';
|
||||
@@ -21,45 +16,23 @@ import Pagination from 'ui/shared/pagination/Pagination';
|
||||
import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages';
|
||||
import getSortParamsFromValue from 'ui/shared/sort/getSortParamsFromValue';
|
||||
import getSortValueFromQuery from 'ui/shared/sort/getSortValueFromQuery';
|
||||
import { sortTxsFromSocket } from 'ui/txs/sortTxs';
|
||||
import TxsWithAPISorting from 'ui/txs/TxsWithAPISorting';
|
||||
import { SORT_OPTIONS } from 'ui/txs/useTxsSort';
|
||||
|
||||
import AddressCsvExportLink from './AddressCsvExportLink';
|
||||
import AddressTxsFilter from './AddressTxsFilter';
|
||||
|
||||
const OVERLOAD_COUNT = 75;
|
||||
|
||||
const getFilterValue = (getFilterValueFromQuery<AddressFromToFilter>).bind(null, AddressFromToFilterValues);
|
||||
|
||||
const matchFilter = (filterValue: AddressFromToFilter, transaction: Transaction, address?: string) => {
|
||||
if (!filterValue) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (filterValue === 'from') {
|
||||
return transaction.from.hash === address;
|
||||
}
|
||||
|
||||
if (filterValue === 'to') {
|
||||
return transaction.to?.hash === address;
|
||||
}
|
||||
};
|
||||
|
||||
type Props = {
|
||||
shouldRender?: boolean;
|
||||
isQueryEnabled?: boolean;
|
||||
// for tests only
|
||||
overloadCount?: number;
|
||||
};
|
||||
|
||||
const AddressTxs = ({ overloadCount = OVERLOAD_COUNT, shouldRender = true, isQueryEnabled = true }: Props) => {
|
||||
const AddressTxs = ({ 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<TransactionsSortingValue>(getSortValueFromQuery<TransactionsSortingValue>(router.query, SORT_OPTIONS) || 'default');
|
||||
|
||||
const isMobile = useIsMobile();
|
||||
@@ -90,76 +63,6 @@ const AddressTxs = ({ overloadCount = OVERLOAD_COUNT, shouldRender = true, isQue
|
||||
addressTxsQuery.onFilterChange({ filter: newVal });
|
||||
}, [ addressTxsQuery ]);
|
||||
|
||||
const handleNewSocketMessage: SocketMessage.AddressTxs['handler'] = React.useCallback((payload) => {
|
||||
setSocketAlert('');
|
||||
|
||||
queryClient.setQueryData(
|
||||
getResourceKey('address_txs', { pathParams: { hash: currentAddress }, queryParams: { filter: filterValue } }),
|
||||
(prevData: AddressTransactionsResponse | undefined) => {
|
||||
if (!prevData) {
|
||||
return;
|
||||
}
|
||||
|
||||
const newItems: Array<Transaction> = [];
|
||||
let newCount = 0;
|
||||
|
||||
payload.transactions.forEach(tx => {
|
||||
const currIndex = prevData.items.findIndex((item) => item.hash === tx.hash);
|
||||
|
||||
if (currIndex > -1) {
|
||||
prevData.items[currIndex] = tx;
|
||||
} else {
|
||||
if (matchFilter(filterValue, tx, currentAddress)) {
|
||||
if (newItems.length + prevData.items.length >= overloadCount) {
|
||||
newCount++;
|
||||
} else {
|
||||
newItems.push(tx);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (newCount > 0) {
|
||||
setNewItemsCount(prev => prev + newCount);
|
||||
}
|
||||
|
||||
return {
|
||||
...prevData,
|
||||
items: [
|
||||
...newItems,
|
||||
...prevData.items,
|
||||
].sort(sortTxsFromSocket(sort)),
|
||||
};
|
||||
});
|
||||
}, [ currentAddress, filterValue, overloadCount, queryClient, sort ]);
|
||||
|
||||
const handleSocketClose = React.useCallback(() => {
|
||||
setSocketAlert('Connection is lost. Please refresh the page to load new transactions.');
|
||||
}, []);
|
||||
|
||||
const handleSocketError = React.useCallback(() => {
|
||||
setSocketAlert('An error has occurred while fetching new transactions. Please refresh the page.');
|
||||
}, []);
|
||||
|
||||
const channel = useSocketChannel({
|
||||
topic: `addresses:${ currentAddress?.toLowerCase() }`,
|
||||
onSocketClose: handleSocketClose,
|
||||
onSocketError: handleSocketError,
|
||||
isDisabled: addressTxsQuery.pagination.page !== 1 || addressTxsQuery.isPlaceholderData,
|
||||
});
|
||||
|
||||
useSocketMessage({
|
||||
channel,
|
||||
event: 'transaction',
|
||||
handler: handleNewSocketMessage,
|
||||
});
|
||||
|
||||
useSocketMessage({
|
||||
channel,
|
||||
event: 'pending_transaction',
|
||||
handler: handleNewSocketMessage,
|
||||
});
|
||||
|
||||
if (!isMounted || !shouldRender) {
|
||||
return null;
|
||||
}
|
||||
@@ -197,9 +100,7 @@ const AddressTxs = ({ overloadCount = OVERLOAD_COUNT, shouldRender = true, isQue
|
||||
query={ addressTxsQuery }
|
||||
currentAddress={ typeof currentAddress === 'string' ? currentAddress : undefined }
|
||||
enableTimeIncrement
|
||||
showSocketInfo={ addressTxsQuery.pagination.page === 1 }
|
||||
socketInfoAlert={ socketAlert }
|
||||
socketInfoNum={ newItemsCount }
|
||||
socketType="address_txs"
|
||||
top={ ACTION_BAR_HEIGHT_DESKTOP }
|
||||
sorting={ sort }
|
||||
setSort={ setSort }
|
||||
|
||||
|
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 42 KiB |
|
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 49 KiB |
|
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 50 KiB |
@@ -87,7 +87,6 @@ const AddressQrCode = ({ hash, className, isLoading }: Props) => {
|
||||
<AddressEntity
|
||||
mb={ 3 }
|
||||
fontWeight={ 500 }
|
||||
color="text"
|
||||
address={{ hash }}
|
||||
noLink
|
||||
/>
|
||||
|
||||
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 67 KiB After Width: | Height: | Size: 67 KiB |
|
Before Width: | Height: | Size: 97 KiB After Width: | Height: | Size: 97 KiB |
|
Before Width: | Height: | Size: 78 KiB After Width: | Height: | Size: 78 KiB |
|
Before Width: | Height: | Size: 84 KiB After Width: | Height: | Size: 84 KiB |
@@ -71,7 +71,7 @@ const FilterByColumn = ({ column, filters, columnName, handleFilterChange, searc
|
||||
<TableColumnFilterWrapper
|
||||
columnName="And/Or"
|
||||
isLoading={ isLoading }
|
||||
selected={ false }
|
||||
selected
|
||||
w="106px"
|
||||
value={ filters.address_relation === 'and' ? 'AND' : 'OR' }
|
||||
>
|
||||
|
||||
@@ -51,10 +51,9 @@ const ContractVerificationForm = ({ method: methodFromQuery, config, hash }: Pro
|
||||
const { handleSubmit, watch, formState, setError, reset, getFieldState, getValues, clearErrors } = formApi;
|
||||
const submitPromiseResolver = React.useRef<(value: unknown) => void>();
|
||||
const methodNameRef = React.useRef<string>();
|
||||
const activityToken = React.useRef<string | undefined>();
|
||||
|
||||
const apiFetch = useApiFetch();
|
||||
const { trackContract, trackContractConfirm } = useRewardsActivity();
|
||||
const { trackContract } = useRewardsActivity();
|
||||
const onFormSubmit: SubmitHandler<FormFields> = React.useCallback(async(data) => {
|
||||
const body = prepareRequestBody(data);
|
||||
|
||||
@@ -78,8 +77,7 @@ const ContractVerificationForm = ({ method: methodFromQuery, config, hash }: Pro
|
||||
}
|
||||
|
||||
try {
|
||||
const activityResponse = await trackContract(data.address);
|
||||
activityToken.current = activityResponse?.token;
|
||||
await trackContract(data.address);
|
||||
await apiFetch('contract_verification_via', {
|
||||
pathParams: { method: data.method[0], hash: data.address.toLowerCase() },
|
||||
fetchParams: {
|
||||
@@ -132,13 +130,8 @@ const ContractVerificationForm = ({ method: methodFromQuery, config, hash }: Pro
|
||||
{ send_immediately: true },
|
||||
);
|
||||
|
||||
if (activityToken.current) {
|
||||
await trackContractConfirm(activityToken.current);
|
||||
activityToken.current = undefined;
|
||||
}
|
||||
|
||||
window.location.assign(route({ pathname: '/address/[hash]', query: { hash: address, tab: 'contract' } }));
|
||||
}, [ setError, address, getValues, trackContractConfirm ]);
|
||||
}, [ setError, address, getValues ]);
|
||||
|
||||
const handleSocketError = React.useCallback(() => {
|
||||
if (!formState.isSubmitting) {
|
||||
|
||||
@@ -14,7 +14,7 @@ import DepositsTableItem from './DepositsTableItem';
|
||||
|
||||
const DepositsTable = ({ items, top, isLoading }: Props) => {
|
||||
return (
|
||||
<TableRoot style={{ tableLayout: 'auto' }} minW="950px">
|
||||
<TableRoot tableLayout="auto" minW="950px">
|
||||
<TableHeaderSticky top={ top }>
|
||||
<TableRow>
|
||||
<TableColumnHeader>L1 block No</TableColumnHeader>
|
||||
@@ -26,7 +26,7 @@ const DepositsTable = ({ items, top, isLoading }: Props) => {
|
||||
</TableHeaderSticky>
|
||||
<TableBody>
|
||||
{ items.map((item, index) => (
|
||||
<DepositsTableItem key={ item.l2_transaction_hash + (isLoading ? index : '') } item={ item } isLoading={ isLoading }/>
|
||||
<DepositsTableItem key={ `${ item.l2_transaction_hash }-${ index }` } item={ item } isLoading={ isLoading }/>
|
||||
)) }
|
||||
</TableBody>
|
||||
</TableRoot>
|
||||
|
||||
@@ -6,6 +6,7 @@ import React from 'react';
|
||||
import config from 'configs/app';
|
||||
import useIsMobile from 'lib/hooks/useIsMobile';
|
||||
import { Button } from 'toolkit/chakra/button';
|
||||
import { Heading } from 'toolkit/chakra/heading';
|
||||
import { Link } from 'toolkit/chakra/link';
|
||||
const easterEggBadgeFeature = config.features.easterEggBadge;
|
||||
|
||||
@@ -36,7 +37,7 @@ const CapybaraRunner = () => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<Box as="h2" mt={ 12 } mb={ 2 } fontWeight={ 600 } fontSize="xl">Score 1000 to win a special prize!</Box>
|
||||
<Heading level="2" mt={ 12 } mb={ 2 }>Score 1000 to win a special prize!</Heading>
|
||||
<Box mb={ 4 }>{ isMobile ? 'Tap below to start' : 'Press space to start' }</Box>
|
||||
<Script strategy="lazyOnload" src="/static/capibara/index.js"/>
|
||||
<Box width={{ base: '100%', lg: '600px' }} height="300px" p="50px 0">
|
||||
|
||||
@@ -6,10 +6,10 @@ import { route } from 'nextjs-routes';
|
||||
import useApiQuery from 'lib/api/useApiQuery';
|
||||
import { AddressHighlightProvider } from 'lib/contexts/addressHighlight';
|
||||
import useIsMobile from 'lib/hooks/useIsMobile';
|
||||
import useNewTxsSocket from 'lib/hooks/useNewTxsSocket';
|
||||
import { TX } from 'stubs/tx';
|
||||
import { Link } from 'toolkit/chakra/link';
|
||||
import SocketNewItemsNotice from 'ui/shared/SocketNewItemsNotice';
|
||||
import useNewTxsSocket from 'ui/txs/socket/useTxsSocketTypeAll';
|
||||
|
||||
import LatestTxsItem from './LatestTxsItem';
|
||||
import LatestTxsItemMobile from './LatestTxsItemMobile';
|
||||
@@ -23,7 +23,7 @@ const LatestTransactions = () => {
|
||||
},
|
||||
});
|
||||
|
||||
const { num, socketAlert } = useNewTxsSocket();
|
||||
const { num, alertText } = useNewTxsSocket({ type: 'txs_home', isLoading: isPlaceholderData });
|
||||
|
||||
if (isError) {
|
||||
return <Text mt={ 4 }>No data. Please reload the page.</Text>;
|
||||
@@ -33,7 +33,7 @@ const LatestTransactions = () => {
|
||||
const txsUrl = route({ pathname: '/txs' });
|
||||
return (
|
||||
<>
|
||||
<SocketNewItemsNotice borderBottomRadius={ 0 } url={ txsUrl } num={ num } alert={ socketAlert } isLoading={ isPlaceholderData }/>
|
||||
<SocketNewItemsNotice borderBottomRadius={ 0 } url={ txsUrl } num={ num } alert={ alertText } isLoading={ isPlaceholderData }/>
|
||||
<Box mb={ 3 } display={{ base: 'block', lg: 'none' }}>
|
||||
{ data.slice(0, txsCount).map(((tx, index) => (
|
||||
<LatestTxsItemMobile
|
||||
|
||||
@@ -126,10 +126,10 @@ const ChainIndicators = () => {
|
||||
alignItems="stretch"
|
||||
>
|
||||
<Flex flexGrow={ 1 } flexDir="column">
|
||||
<Flex alignItems="center">
|
||||
<Skeleton loading={ isPlaceholderData } display="flex" alignItems="center" w="fit-content" columnGap={ 1 }>
|
||||
<Text fontWeight={ 500 }>{ title }</Text>
|
||||
{ hint && <Hint label={ hint } ml={ 1 }/> }
|
||||
</Flex>
|
||||
{ hint && <Hint label={ hint }/> }
|
||||
</Skeleton>
|
||||
<Flex mb={{ base: 0, lg: 2 }} mt={ 1 } alignItems="end">
|
||||
{ valueTitle }
|
||||
{ valueDiff }
|
||||
|
||||
@@ -17,13 +17,13 @@ interface Props {
|
||||
const InternalTxsTable = ({ data, currentAddress, isLoading }: Props) => {
|
||||
return (
|
||||
<AddressHighlightProvider>
|
||||
<TableRoot>
|
||||
<TableRoot minW="900px">
|
||||
<TableHeaderSticky top={ 68 }>
|
||||
<TableRow>
|
||||
<TableColumnHeader width="15%">Parent txn hash</TableColumnHeader>
|
||||
<TableColumnHeader width="180px">Parent txn hash</TableColumnHeader>
|
||||
<TableColumnHeader width="15%">Type</TableColumnHeader>
|
||||
<TableColumnHeader width="10%">Block</TableColumnHeader>
|
||||
<TableColumnHeader width="40%">From/To</TableColumnHeader>
|
||||
<TableColumnHeader width="15%">Block</TableColumnHeader>
|
||||
<TableColumnHeader width="50%">From/To</TableColumnHeader>
|
||||
<TableColumnHeader width="20%" isNumeric>
|
||||
Value { currencyUnits.ether }
|
||||
</TableColumnHeader>
|
||||
|
||||
@@ -6,13 +6,13 @@ import type { InternalTransaction } from 'types/api/internalTransaction';
|
||||
|
||||
import config from 'configs/app';
|
||||
import { Badge } from 'toolkit/chakra/badge';
|
||||
import { Skeleton } from 'toolkit/chakra/skeleton';
|
||||
import { TableCell, TableRow } from 'toolkit/chakra/table';
|
||||
import AddressFromTo from 'ui/shared/address/AddressFromTo';
|
||||
import BlockEntity from 'ui/shared/entities/block/BlockEntity';
|
||||
import TxEntity from 'ui/shared/entities/tx/TxEntity';
|
||||
import TxStatus from 'ui/shared/statusTag/TxStatus';
|
||||
import TimeAgoWithTooltip from 'ui/shared/TimeAgoWithTooltip';
|
||||
import TruncatedValue from 'ui/shared/TruncatedValue';
|
||||
import { TX_INTERNALS_ITEMS } from 'ui/tx/internals/utils';
|
||||
|
||||
type Props = InternalTransaction & { currentAddress?: string; isLoading?: boolean };
|
||||
@@ -81,9 +81,13 @@ const InternalTxsTableItem = ({
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell isNumeric verticalAlign="middle">
|
||||
<Skeleton loading={ isLoading } display="inline-block" minW={ 6 }>
|
||||
{ BigNumber(value).div(BigNumber(10 ** config.chain.currency.decimals)).toFormat() }
|
||||
</Skeleton>
|
||||
<TruncatedValue
|
||||
value={ BigNumber(value).div(BigNumber(10 ** config.chain.currency.decimals)).toFormat() }
|
||||
isLoading={ isLoading }
|
||||
minW={ 6 }
|
||||
maxW="100%"
|
||||
verticalAlign="middle"
|
||||
/>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
);
|
||||
|
||||
@@ -12,6 +12,7 @@ import config from 'configs/app';
|
||||
import solidityScanIcon from 'icons/brands/solidity_scan.svg';
|
||||
import * as mixpanel from 'lib/mixpanel/index';
|
||||
import { Link } from 'toolkit/chakra/link';
|
||||
import type { PopoverContentProps } from 'toolkit/chakra/popover';
|
||||
import { PopoverBody, PopoverContent, PopoverRoot } from 'toolkit/chakra/popover';
|
||||
import { useDisclosure } from 'toolkit/hooks/useDisclosure';
|
||||
import { apos } from 'toolkit/utils/htmlEntities';
|
||||
@@ -30,10 +31,20 @@ type Props = {
|
||||
popoverPlacement?: 'bottom-start' | 'bottom-end' | 'left';
|
||||
buttonProps?: ButtonProps;
|
||||
triggerWrapperProps?: BoxProps;
|
||||
popoverContentProps?: PopoverContentProps;
|
||||
};
|
||||
|
||||
const AppSecurityReport = ({
|
||||
id, securityReport, showContractList, isLoading, onlyIcon, source, triggerWrapperProps, buttonProps, popoverPlacement = 'bottom-start',
|
||||
id,
|
||||
securityReport,
|
||||
showContractList,
|
||||
isLoading,
|
||||
onlyIcon,
|
||||
source,
|
||||
triggerWrapperProps,
|
||||
buttonProps,
|
||||
popoverPlacement = 'bottom-start',
|
||||
popoverContentProps,
|
||||
}: Props) => {
|
||||
|
||||
const { open, onOpenChange } = useDisclosure();
|
||||
@@ -76,7 +87,7 @@ const AppSecurityReport = ({
|
||||
wrapperProps={ triggerWrapperProps }
|
||||
{ ...buttonProps }
|
||||
/>
|
||||
<PopoverContent w={{ base: 'calc(100vw - 48px)', lg: '328px' }} mx={{ base: 3, lg: 0 }}>
|
||||
<PopoverContent w={{ base: 'calc(100vw - 48px)', lg: '328px' }} mx={{ base: 3, lg: 0 }} { ...popoverContentProps }>
|
||||
<PopoverBody px="26px" py="20px" textStyle="sm">
|
||||
<Text fontWeight="500" textStyle="xs" mb={ 2 } color="text.secondary">Smart contracts info</Text>
|
||||
<Flex alignItems="center" justifyContent="space-between" py={ 1.5 }>
|
||||
|
||||
@@ -58,7 +58,6 @@ const ContractListModal = ({ onClose, onBack, type, contracts }: Props) => {
|
||||
open={ Boolean(type) }
|
||||
onOpenChange={ handleOpenChange }
|
||||
size={{ lgDown: 'full', lg: 'md' }}
|
||||
placement="center"
|
||||
>
|
||||
<DialogContent>
|
||||
<DialogHeader display="flex" alignItems="center" mb={ 4 } onBackToClick={ onBack }>
|
||||
|
||||
@@ -135,7 +135,6 @@ const MarketplaceAppModal = ({
|
||||
open={ Boolean(data.id) }
|
||||
onOpenChange={ handleOpenChange }
|
||||
size={{ lgDown: 'full', lg: 'md' }}
|
||||
placement="center"
|
||||
>
|
||||
<DialogContent>
|
||||
<Box
|
||||
@@ -197,6 +196,7 @@ const MarketplaceAppModal = ({
|
||||
fullView
|
||||
canRate={ canRate }
|
||||
source="App modal"
|
||||
popoverContentProps={{ zIndex: 'modal' }}
|
||||
/>
|
||||
</Box>
|
||||
) }
|
||||
@@ -265,6 +265,7 @@ const MarketplaceAppModal = ({
|
||||
showContractList={ showContractList }
|
||||
source="App modal"
|
||||
popoverPlacement={ isMobile ? 'bottom-start' : 'left' }
|
||||
popoverContentProps={{ zIndex: 'modal' }}
|
||||
/>
|
||||
</Flex>
|
||||
</Flex>
|
||||
|
||||
@@ -27,7 +27,6 @@ const MarketplaceDisclaimerModal = ({ isOpen, onClose, appId }: Props) => {
|
||||
open={ isOpen }
|
||||
onOpenChange={ onClose }
|
||||
size={ isMobile ? 'full' : 'md' }
|
||||
placement="center"
|
||||
>
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
|
||||
@@ -5,6 +5,7 @@ import type { AppRating } from 'types/client/marketplace';
|
||||
|
||||
import config from 'configs/app';
|
||||
import type { EventTypes, EventPayload } from 'lib/mixpanel/index';
|
||||
import type { PopoverContentProps } from 'toolkit/chakra/popover';
|
||||
import { PopoverBody, PopoverContent, PopoverRoot } from 'toolkit/chakra/popover';
|
||||
import { Rating } from 'toolkit/chakra/rating';
|
||||
import { Skeleton } from 'toolkit/chakra/skeleton';
|
||||
@@ -26,11 +27,13 @@ type Props = {
|
||||
fullView?: boolean;
|
||||
canRate: boolean | undefined;
|
||||
source: EventPayload<EventTypes.APP_FEEDBACK>['Source'];
|
||||
popoverContentProps?: PopoverContentProps;
|
||||
};
|
||||
|
||||
const MarketplaceRating = ({
|
||||
appId, rating, userRating, rate,
|
||||
isSending, isLoading, fullView, canRate, source,
|
||||
popoverContentProps,
|
||||
}: Props) => {
|
||||
|
||||
if (!isEnabled) {
|
||||
@@ -60,7 +63,7 @@ const MarketplaceRating = ({
|
||||
canRate={ canRate }
|
||||
/>
|
||||
{ canRate ? (
|
||||
<PopoverContent w="250px">
|
||||
<PopoverContent w="250px" { ...popoverContentProps }>
|
||||
<PopoverBody>
|
||||
<Content
|
||||
appId={ appId }
|
||||
|
||||
@@ -137,7 +137,7 @@ const AdvancedFilter = () => {
|
||||
|
||||
const content = (
|
||||
<AddressHighlightProvider>
|
||||
<Box maxW="100%" overflowX="scroll" whiteSpace="nowrap">
|
||||
<Box maxW="100%" display="grid" overflowX="scroll" whiteSpace="nowrap">
|
||||
<TableRoot tableLayout="fixed" minWidth="950px" w="100%">
|
||||
<TableHeaderSticky>
|
||||
<TableRow>
|
||||
|
||||
@@ -12,7 +12,6 @@ import { BLOCK } from 'stubs/block';
|
||||
import { TX } from 'stubs/tx';
|
||||
import { generateListStub } from 'stubs/utils';
|
||||
import RoutedTabs from 'toolkit/components/RoutedTabs/RoutedTabs';
|
||||
import RoutedTabsSkeleton from 'toolkit/components/RoutedTabs/RoutedTabsSkeleton';
|
||||
import BlocksContent from 'ui/blocks/BlocksContent';
|
||||
import TextAd from 'ui/shared/ad/TextAd';
|
||||
import PageTitle from 'ui/shared/Page/PageTitle';
|
||||
@@ -86,7 +85,7 @@ const ArbitrumL2TxnBatch = () => {
|
||||
{
|
||||
id: 'txs',
|
||||
title: 'Transactions',
|
||||
component: <TxsWithFrontendSorting query={ batchTxsQuery } showSocketInfo={ false } top={ hasPagination ? TABS_HEIGHT : 0 }/>,
|
||||
component: <TxsWithFrontendSorting query={ batchTxsQuery } top={ hasPagination ? TABS_HEIGHT : 0 }/>,
|
||||
},
|
||||
{
|
||||
id: 'blocks',
|
||||
@@ -116,15 +115,13 @@ const ArbitrumL2TxnBatch = () => {
|
||||
backLink={ backLink }
|
||||
isLoading={ batchQuery.isPlaceholderData }
|
||||
/>
|
||||
{ batchQuery.isPlaceholderData ?
|
||||
<RoutedTabsSkeleton tabs={ tabs }/> : (
|
||||
<RoutedTabs
|
||||
tabs={ tabs }
|
||||
listProps={ isMobile ? undefined : TAB_LIST_PROPS }
|
||||
rightSlot={ hasPagination && pagination ? <Pagination { ...(pagination) }/> : null }
|
||||
stickyEnabled={ hasPagination }
|
||||
/>
|
||||
) }
|
||||
<RoutedTabs
|
||||
tabs={ tabs }
|
||||
isLoading={ batchQuery.isPlaceholderData }
|
||||
listProps={ isMobile ? undefined : TAB_LIST_PROPS }
|
||||
rightSlot={ hasPagination && pagination ? <Pagination { ...(pagination) }/> : null }
|
||||
stickyEnabled={ hasPagination }
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -15,7 +15,6 @@ import getNetworkValidationActionText from 'lib/networks/getNetworkValidationAct
|
||||
import getQueryParamString from 'lib/router/getQueryParamString';
|
||||
import { Skeleton } from 'toolkit/chakra/skeleton';
|
||||
import RoutedTabs from 'toolkit/components/RoutedTabs/RoutedTabs';
|
||||
import RoutedTabsSkeleton from 'toolkit/components/RoutedTabs/RoutedTabsSkeleton';
|
||||
import BlockCeloEpochTag from 'ui/block/BlockCeloEpochTag';
|
||||
import BlockDetails from 'ui/block/BlockDetails';
|
||||
import BlockEpochRewards from 'ui/block/BlockEpochRewards';
|
||||
@@ -74,7 +73,7 @@ const BlockPageContent = () => {
|
||||
component: (
|
||||
<>
|
||||
{ blockTxsQuery.isDegradedData && <ServiceDegradationWarning isLoading={ blockTxsQuery.isPlaceholderData } mb={ 6 }/> }
|
||||
<TxsWithFrontendSorting query={ blockTxsQuery } showBlockInfo={ false } showSocketInfo={ false } top={ hasPagination ? TABS_HEIGHT : 0 }/>
|
||||
<TxsWithFrontendSorting query={ blockTxsQuery } showBlockInfo={ false } top={ hasPagination ? TABS_HEIGHT : 0 }/>
|
||||
</>
|
||||
),
|
||||
},
|
||||
@@ -83,7 +82,7 @@ const BlockPageContent = () => {
|
||||
id: 'blob_txs',
|
||||
title: 'Blob txns',
|
||||
component: (
|
||||
<TxsWithFrontendSorting query={ blockBlobTxsQuery } showBlockInfo={ false } showSocketInfo={ false }/>
|
||||
<TxsWithFrontendSorting query={ blockBlobTxsQuery } showBlockInfo={ false }/>
|
||||
),
|
||||
} : null,
|
||||
config.features.beaconChain.isEnabled && Boolean(blockQuery.data?.withdrawals_count) ?
|
||||
@@ -183,14 +182,13 @@ const BlockPageContent = () => {
|
||||
secondRow={ titleSecondRow }
|
||||
isLoading={ blockQuery.isPlaceholderData }
|
||||
/>
|
||||
{ blockQuery.isPlaceholderData ? <RoutedTabsSkeleton tabs={ tabs }/> : (
|
||||
<RoutedTabs
|
||||
tabs={ tabs }
|
||||
listProps={ isMobile ? undefined : TAB_LIST_PROPS }
|
||||
rightSlot={ hasPagination ? <Pagination { ...(pagination as PaginationParams) }/> : null }
|
||||
stickyEnabled={ hasPagination }
|
||||
/>
|
||||
) }
|
||||
<RoutedTabs
|
||||
tabs={ tabs }
|
||||
isLoading={ blockQuery.isPlaceholderData }
|
||||
listProps={ isMobile ? undefined : TAB_LIST_PROPS }
|
||||
rightSlot={ hasPagination ? <Pagination { ...(pagination as PaginationParams) }/> : null }
|
||||
stickyEnabled={ hasPagination }
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -31,10 +31,7 @@ const KettleTxs = () => {
|
||||
<>
|
||||
<PageTitle title="Computor transactions" withTextAd/>
|
||||
<AddressEntity address={{ hash }} mb={ 6 }/>
|
||||
<TxsWithFrontendSorting
|
||||
query={ query }
|
||||
showSocketInfo={ false }
|
||||
/>
|
||||
<TxsWithFrontendSorting query={ query }/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -170,7 +170,7 @@ const Marketplace = () => {
|
||||
<IconSvg name="dots"/>
|
||||
</IconButton>
|
||||
</MenuTrigger>
|
||||
<MenuContent>
|
||||
<MenuContent zIndex="banner">
|
||||
{ links.map(({ label, href, icon }) => (
|
||||
<MenuItem key={ label } value={ label } asChild>
|
||||
<Link external href={ href } variant="menu" gap={ 0 }>
|
||||
@@ -205,6 +205,7 @@ const Marketplace = () => {
|
||||
showShadow
|
||||
display="flex"
|
||||
flexDirection="column"
|
||||
mt={ 0 }
|
||||
mx={{ base: -3, lg: -12 }}
|
||||
px={{ base: 3, lg: 12 }}
|
||||
pt={{ base: 4, lg: 6 }}
|
||||
|
||||
@@ -14,8 +14,6 @@ import { ENS_DOMAIN } from 'stubs/ENS';
|
||||
import { Link } from 'toolkit/chakra/link';
|
||||
import { Tooltip } from 'toolkit/chakra/tooltip';
|
||||
import RoutedTabs from 'toolkit/components/RoutedTabs/RoutedTabs';
|
||||
import RoutedTabsSkeleton from 'toolkit/components/RoutedTabs/RoutedTabsSkeleton';
|
||||
import useActiveTabFromQuery from 'toolkit/components/RoutedTabs/useActiveTabFromQuery';
|
||||
import NameDomainDetails from 'ui/nameDomain/NameDomainDetails';
|
||||
import NameDomainHistory from 'ui/nameDomain/NameDomainHistory';
|
||||
import TextAd from 'ui/shared/ad/TextAd';
|
||||
@@ -40,8 +38,6 @@ const NameDomain = () => {
|
||||
{ id: 'history', title: 'History', component: <NameDomainHistory domain={ infoQuery.data }/> },
|
||||
];
|
||||
|
||||
const activeTab = useActiveTabFromQuery(tabs);
|
||||
|
||||
throwOnResourceLoadError(infoQuery);
|
||||
|
||||
const isLoading = infoQuery.isPlaceholderData;
|
||||
@@ -87,12 +83,7 @@ const NameDomain = () => {
|
||||
<>
|
||||
<TextAd mb={ 6 }/>
|
||||
<PageTitle title="Name details" secondRow={ titleSecondRow }/>
|
||||
{ infoQuery.isPlaceholderData ? (
|
||||
<>
|
||||
<RoutedTabsSkeleton tabs={ tabs } mt={ 6 }/>
|
||||
{ activeTab?.component }
|
||||
</>
|
||||
) : <RoutedTabs tabs={ tabs }/> }
|
||||
<RoutedTabs tabs={ tabs } isLoading={ infoQuery.isPlaceholderData }/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -12,7 +12,6 @@ import { BLOCK } from 'stubs/block';
|
||||
import { TX } from 'stubs/tx';
|
||||
import { generateListStub } from 'stubs/utils';
|
||||
import RoutedTabs from 'toolkit/components/RoutedTabs/RoutedTabs';
|
||||
import RoutedTabsSkeleton from 'toolkit/components/RoutedTabs/RoutedTabsSkeleton';
|
||||
import BlocksContent from 'ui/blocks/BlocksContent';
|
||||
import TextAd from 'ui/shared/ad/TextAd';
|
||||
import PageTitle from 'ui/shared/Page/PageTitle';
|
||||
@@ -84,7 +83,7 @@ const OptimisticL2TxnBatch = () => {
|
||||
{
|
||||
id: 'txs',
|
||||
title: 'Transactions',
|
||||
component: <TxsWithFrontendSorting query={ batchTxsQuery } showSocketInfo={ false } top={ hasPagination ? TABS_HEIGHT : 0 }/>,
|
||||
component: <TxsWithFrontendSorting query={ batchTxsQuery } top={ hasPagination ? TABS_HEIGHT : 0 }/>,
|
||||
},
|
||||
{
|
||||
id: 'blocks',
|
||||
@@ -114,15 +113,13 @@ const OptimisticL2TxnBatch = () => {
|
||||
backLink={ backLink }
|
||||
isLoading={ batchQuery.isPlaceholderData }
|
||||
/>
|
||||
{ batchQuery.isPlaceholderData ?
|
||||
<RoutedTabsSkeleton tabs={ tabs }/> : (
|
||||
<RoutedTabs
|
||||
tabs={ tabs }
|
||||
listProps={ isMobile ? undefined : TAB_LIST_PROPS }
|
||||
rightSlot={ hasPagination && pagination ? <Pagination { ...(pagination) }/> : null }
|
||||
stickyEnabled={ hasPagination }
|
||||
/>
|
||||
) }
|
||||
<RoutedTabs
|
||||
tabs={ tabs }
|
||||
isLoading={ batchQuery.isPlaceholderData }
|
||||
listProps={ isMobile ? undefined : TAB_LIST_PROPS }
|
||||
rightSlot={ hasPagination && pagination ? <Pagination { ...(pagination) }/> : null }
|
||||
stickyEnabled={ hasPagination }
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -14,7 +14,6 @@ import { SCROLL_L2_TXN_BATCH } from 'stubs/scrollL2';
|
||||
import { TX } from 'stubs/tx';
|
||||
import { generateListStub } from 'stubs/utils';
|
||||
import RoutedTabs from 'toolkit/components/RoutedTabs/RoutedTabs';
|
||||
import RoutedTabsSkeleton from 'toolkit/components/RoutedTabs/RoutedTabsSkeleton';
|
||||
import BlocksContent from 'ui/blocks/BlocksContent';
|
||||
import TextAd from 'ui/shared/ad/TextAd';
|
||||
import PageTitle from 'ui/shared/Page/PageTitle';
|
||||
@@ -91,7 +90,7 @@ const ScrollL2TxnBatch = () => {
|
||||
{
|
||||
id: 'txs',
|
||||
title: 'Transactions',
|
||||
component: <TxsWithFrontendSorting query={ batchTxsQuery } showSocketInfo={ false } top={ hasPagination ? TABS_HEIGHT : 0 }/>,
|
||||
component: <TxsWithFrontendSorting query={ batchTxsQuery } top={ hasPagination ? TABS_HEIGHT : 0 }/>,
|
||||
},
|
||||
{
|
||||
id: 'blocks',
|
||||
@@ -120,15 +119,13 @@ const ScrollL2TxnBatch = () => {
|
||||
title={ `Txn batch #${ number }` }
|
||||
backLink={ backLink }
|
||||
/>
|
||||
{ batchQuery.isPlaceholderData ?
|
||||
<RoutedTabsSkeleton tabs={ tabs }/> : (
|
||||
<RoutedTabs
|
||||
tabs={ tabs }
|
||||
listProps={ isMobile ? undefined : TAB_LIST_PROPS }
|
||||
rightSlot={ hasPagination && pagination ? <Pagination { ...(pagination) }/> : null }
|
||||
stickyEnabled={ hasPagination }
|
||||
/>
|
||||
) }
|
||||
<RoutedTabs
|
||||
tabs={ tabs }
|
||||
isLoading={ batchQuery.isPlaceholderData }
|
||||
listProps={ isMobile ? undefined : TAB_LIST_PROPS }
|
||||
rightSlot={ hasPagination && pagination ? <Pagination { ...(pagination) }/> : null }
|
||||
stickyEnabled={ hasPagination }
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -42,7 +42,7 @@ const L2Deposits = () => {
|
||||
<Box hideFrom="lg">
|
||||
{ data.items.map(((item, index) => (
|
||||
<DepositsListItem
|
||||
key={ item.l2_transaction_hash + (isPlaceholderData ? index : '') }
|
||||
key={ `${ item.l2_transaction_hash }-${ index }` }
|
||||
isLoading={ isPlaceholderData }
|
||||
item={ item }
|
||||
/>
|
||||
|
||||
@@ -14,7 +14,7 @@ import StickyPaginationWithText from 'ui/shared/StickyPaginationWithText';
|
||||
import WithdrawalsListItem from 'ui/withdrawals/shibarium/WithdrawalsListItem';
|
||||
import WithdrawalsTable from 'ui/withdrawals/shibarium/WithdrawalsTable';
|
||||
|
||||
const L2Withdrawals = () => {
|
||||
const ShibariumWithdrawals = () => {
|
||||
const { data, isError, isPlaceholderData, pagination } = useQueryWithPages({
|
||||
resourceName: 'shibarium_withdrawals',
|
||||
options: {
|
||||
@@ -42,7 +42,7 @@ const L2Withdrawals = () => {
|
||||
<Box hideFrom="lg">
|
||||
{ data.items.map(((item, index) => (
|
||||
<WithdrawalsListItem
|
||||
key={ item.l2_transaction_hash + (isPlaceholderData ? index : '') }
|
||||
key={ `${ item.l2_transaction_hash }-${ index }` }
|
||||
item={ item }
|
||||
isLoading={ isPlaceholderData }
|
||||
/>
|
||||
@@ -83,4 +83,4 @@ const L2Withdrawals = () => {
|
||||
);
|
||||
};
|
||||
|
||||
export default L2Withdrawals;
|
||||
export default ShibariumWithdrawals;
|
||||
|
||||
@@ -10,8 +10,6 @@ import throwOnResourceLoadError from 'lib/errors/throwOnResourceLoadError';
|
||||
import getQueryParamString from 'lib/router/getQueryParamString';
|
||||
import { publicClient } from 'lib/web3/client';
|
||||
import RoutedTabs from 'toolkit/components/RoutedTabs/RoutedTabs';
|
||||
import RoutedTabsSkeleton from 'toolkit/components/RoutedTabs/RoutedTabsSkeleton';
|
||||
import useActiveTabFromQuery from 'toolkit/components/RoutedTabs/useActiveTabFromQuery';
|
||||
import TextAd from 'ui/shared/ad/TextAd';
|
||||
import isCustomAppError from 'ui/shared/AppError/isCustomAppError';
|
||||
import EntityTags from 'ui/shared/EntityTags/EntityTags';
|
||||
@@ -78,8 +76,6 @@ const TransactionPageContent = () => {
|
||||
].filter(Boolean);
|
||||
})();
|
||||
|
||||
const activeTab = useActiveTabFromQuery(tabs);
|
||||
|
||||
const txTags: Array<TEntityTag> = data?.transaction_tag ?
|
||||
[ { slug: data.transaction_tag, name: data.transaction_tag, tagType: 'private_tag' as const, ordinal: 1 } ] : [];
|
||||
if (rollupFeature.isEnabled && rollupFeature.interopEnabled && data?.op_interop) {
|
||||
@@ -113,19 +109,6 @@ const TransactionPageContent = () => {
|
||||
|
||||
const titleSecondRow = <TxSubHeading hash={ hash } hasTag={ Boolean(data?.transaction_tag) } txQuery={ txQuery }/>;
|
||||
|
||||
const content = (() => {
|
||||
if (isPlaceholderData && !showDegradedView) {
|
||||
return (
|
||||
<>
|
||||
<RoutedTabsSkeleton tabs={ tabs } mt={ 6 }/>
|
||||
{ activeTab?.component }
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
return <RoutedTabs tabs={ tabs }/>;
|
||||
})();
|
||||
|
||||
if (isError && !showDegradedView) {
|
||||
if (isCustomAppError(error)) {
|
||||
throwOnResourceLoadError({ resource: 'tx', error, isError: true });
|
||||
@@ -141,7 +124,7 @@ const TransactionPageContent = () => {
|
||||
contentAfter={ tags }
|
||||
secondRow={ titleSecondRow }
|
||||
/>
|
||||
{ content }
|
||||
<RoutedTabs tabs={ tabs } isLoading={ isPlaceholderData }/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -9,7 +9,6 @@ import { route } from 'nextjs-routes';
|
||||
|
||||
import config from 'configs/app';
|
||||
import useIsMobile from 'lib/hooks/useIsMobile';
|
||||
import useNewTxsSocket from 'lib/hooks/useNewTxsSocket';
|
||||
import getNetworkValidationActionText from 'lib/networks/getNetworkValidationActionText';
|
||||
import getQueryParamString from 'lib/router/getQueryParamString';
|
||||
import { TX } from 'stubs/tx';
|
||||
@@ -91,8 +90,6 @@ const Transactions = () => {
|
||||
},
|
||||
});
|
||||
|
||||
const { num, socketAlert } = useNewTxsSocket();
|
||||
|
||||
const isAuth = useIsAuth();
|
||||
|
||||
const tabs: Array<TabItemRegular> = [
|
||||
@@ -102,9 +99,7 @@ const Transactions = () => {
|
||||
component:
|
||||
<TxsWithFrontendSorting
|
||||
query={ txsValidatedQuery }
|
||||
showSocketInfo={ txsValidatedQuery.pagination.page === 1 }
|
||||
socketInfoNum={ num }
|
||||
socketInfoAlert={ socketAlert }
|
||||
socketType="txs_validated"
|
||||
top={ TABS_HEIGHT }
|
||||
/> },
|
||||
{
|
||||
@@ -114,9 +109,7 @@ const Transactions = () => {
|
||||
<TxsWithFrontendSorting
|
||||
query={ txsPendingQuery }
|
||||
showBlockInfo={ false }
|
||||
showSocketInfo={ txsPendingQuery.pagination.page === 1 }
|
||||
socketInfoNum={ num }
|
||||
socketInfoAlert={ socketAlert }
|
||||
socketType="txs_pending"
|
||||
top={ TABS_HEIGHT }
|
||||
/>
|
||||
),
|
||||
@@ -127,9 +120,6 @@ const Transactions = () => {
|
||||
component: (
|
||||
<TxsWithFrontendSorting
|
||||
query={ txsWithBlobsQuery }
|
||||
showSocketInfo={ txsWithBlobsQuery.pagination.page === 1 }
|
||||
socketInfoNum={ num }
|
||||
socketInfoAlert={ socketAlert }
|
||||
top={ TABS_HEIGHT }
|
||||
/>
|
||||
),
|
||||
|
||||
@@ -13,8 +13,6 @@ import throwOnResourceLoadError from 'lib/errors/throwOnResourceLoadError';
|
||||
import getQueryParamString from 'lib/router/getQueryParamString';
|
||||
import { USER_OP } from 'stubs/userOps';
|
||||
import RoutedTabs from 'toolkit/components/RoutedTabs/RoutedTabs';
|
||||
import RoutedTabsSkeleton from 'toolkit/components/RoutedTabs/RoutedTabsSkeleton';
|
||||
import useActiveTabFromQuery from 'toolkit/components/RoutedTabs/useActiveTabFromQuery';
|
||||
import TextAd from 'ui/shared/ad/TextAd';
|
||||
import PageTitle from 'ui/shared/Page/PageTitle';
|
||||
import TxLogs from 'ui/tx/TxLogs';
|
||||
@@ -82,8 +80,6 @@ const UserOp = () => {
|
||||
{ id: 'raw', title: 'Raw', component: <UserOpRaw rawData={ userOpQuery.data?.raw } isLoading={ userOpQuery.isPlaceholderData }/> },
|
||||
]), [ userOpQuery, txQuery, filterTokenTransfersByLogIndex, filterLogsByLogIndex ]);
|
||||
|
||||
const activeTab = useActiveTabFromQuery(tabs);
|
||||
|
||||
const backLink = React.useMemo(() => {
|
||||
const hasGoBackLink = appProps.referrer && appProps.referrer.includes('/ops');
|
||||
|
||||
@@ -110,13 +106,7 @@ const UserOp = () => {
|
||||
backLink={ backLink }
|
||||
secondRow={ titleSecondRow }
|
||||
/>
|
||||
{ userOpQuery.isPlaceholderData ? (
|
||||
<>
|
||||
<RoutedTabsSkeleton tabs={ tabs } mt={ 6 }/>
|
||||
{ activeTab?.component }
|
||||
</>
|
||||
) :
|
||||
<RoutedTabs tabs={ tabs }/> }
|
||||
<RoutedTabs tabs={ tabs } isLoading={ userOpQuery.isPlaceholderData }/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -12,7 +12,6 @@ import { TX_ZKEVM_L2 } from 'stubs/tx';
|
||||
import { generateListStub } from 'stubs/utils';
|
||||
import { ZKEVM_L2_TXN_BATCH } from 'stubs/zkEvmL2';
|
||||
import RoutedTabs from 'toolkit/components/RoutedTabs/RoutedTabs';
|
||||
import RoutedTabsSkeleton from 'toolkit/components/RoutedTabs/RoutedTabsSkeleton';
|
||||
import TextAd from 'ui/shared/ad/TextAd';
|
||||
import PageTitle from 'ui/shared/Page/PageTitle';
|
||||
import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages';
|
||||
@@ -48,7 +47,7 @@ const ZkEvmL2TxnBatch = () => {
|
||||
|
||||
const tabs: Array<TabItemRegular> = React.useMemo(() => ([
|
||||
{ id: 'index', title: 'Details', component: <ZkEvmL2TxnBatchDetails query={ batchQuery }/> },
|
||||
{ id: 'txs', title: 'Transactions', component: <TxsWithFrontendSorting query={ batchTxsQuery } showSocketInfo={ false }/> },
|
||||
{ id: 'txs', title: 'Transactions', component: <TxsWithFrontendSorting query={ batchTxsQuery }/> },
|
||||
].filter(Boolean)), [ batchQuery, batchTxsQuery ]);
|
||||
|
||||
const backLink = React.useMemo(() => {
|
||||
@@ -71,11 +70,10 @@ const ZkEvmL2TxnBatch = () => {
|
||||
title={ `Txn batch #${ number }` }
|
||||
backLink={ backLink }
|
||||
/>
|
||||
{ batchQuery.isPlaceholderData ? <RoutedTabsSkeleton tabs={ tabs }/> : (
|
||||
<RoutedTabs
|
||||
tabs={ tabs }
|
||||
/>
|
||||
) }
|
||||
<RoutedTabs
|
||||
tabs={ tabs }
|
||||
isLoading={ batchQuery.isPlaceholderData }
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -13,7 +13,6 @@ import { TX } from 'stubs/tx';
|
||||
import { generateListStub } from 'stubs/utils';
|
||||
import { ZKSYNC_L2_TXN_BATCH } from 'stubs/zkSyncL2';
|
||||
import RoutedTabs from 'toolkit/components/RoutedTabs/RoutedTabs';
|
||||
import RoutedTabsSkeleton from 'toolkit/components/RoutedTabs/RoutedTabsSkeleton';
|
||||
import TextAd from 'ui/shared/ad/TextAd';
|
||||
import PageTitle from 'ui/shared/Page/PageTitle';
|
||||
import Pagination from 'ui/shared/pagination/Pagination';
|
||||
@@ -68,7 +67,7 @@ const ZkSyncL2TxnBatch = () => {
|
||||
{
|
||||
id: 'txs',
|
||||
title: 'Transactions',
|
||||
component: <TxsWithFrontendSorting query={ batchTxsQuery } showSocketInfo={ false } top={ hasPagination ? TABS_HEIGHT : 0 }/>,
|
||||
component: <TxsWithFrontendSorting query={ batchTxsQuery } top={ hasPagination ? TABS_HEIGHT : 0 }/>,
|
||||
},
|
||||
].filter(Boolean)), [ batchQuery, batchTxsQuery, hasPagination ]);
|
||||
|
||||
@@ -92,15 +91,13 @@ const ZkSyncL2TxnBatch = () => {
|
||||
title={ `Txn batch #${ number }` }
|
||||
backLink={ backLink }
|
||||
/>
|
||||
{ batchQuery.isPlaceholderData ?
|
||||
<RoutedTabsSkeleton tabs={ tabs }/> : (
|
||||
<RoutedTabs
|
||||
tabs={ tabs }
|
||||
listProps={ isMobile ? undefined : TAB_LIST_PROPS }
|
||||
rightSlot={ hasPagination ? <Pagination { ...(batchTxsQuery.pagination) }/> : null }
|
||||
stickyEnabled={ hasPagination }
|
||||
/>
|
||||
) }
|
||||
<RoutedTabs
|
||||
tabs={ tabs }
|
||||
isLoading={ batchQuery.isPlaceholderData }
|
||||
listProps={ isMobile ? undefined : TAB_LIST_PROPS }
|
||||
rightSlot={ hasPagination ? <Pagination { ...(batchTxsQuery.pagination) }/> : null }
|
||||
stickyEnabled={ hasPagination }
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 54 KiB |
|
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 54 KiB |
|
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 53 KiB |
|
Before Width: | Height: | Size: 69 KiB After Width: | Height: | Size: 69 KiB |
@@ -14,6 +14,7 @@ import getErrorObj from 'lib/errors/getErrorObj';
|
||||
import getErrorObjPayload from 'lib/errors/getErrorObjPayload';
|
||||
import useIsMobile from 'lib/hooks/useIsMobile';
|
||||
import { Button } from 'toolkit/chakra/button';
|
||||
import { Heading } from 'toolkit/chakra/heading';
|
||||
import { FormFieldEmail } from 'toolkit/components/forms/fields/FormFieldEmail';
|
||||
import { FormFieldText } from 'toolkit/components/forms/fields/FormFieldText';
|
||||
import { FormFieldUrl } from 'toolkit/components/forms/fields/FormFieldUrl';
|
||||
@@ -96,8 +97,10 @@ const PublicTagsSubmitForm = ({ config, userInfo, onSubmitResult }: Props) => {
|
||||
rowGap={ 3 }
|
||||
templateColumns={{ base: '1fr', lg: '1fr 1fr minmax(0, 200px)', xl: '1fr 1fr minmax(0, 250px)' }}
|
||||
>
|
||||
<GridItem colSpan={{ base: 1, lg: 3 }} as="h2" textStyle="h4">
|
||||
Company info
|
||||
<GridItem colSpan={{ base: 1, lg: 3 }}>
|
||||
<Heading level="2">
|
||||
Company info
|
||||
</Heading>
|
||||
</GridItem>
|
||||
<FormFieldText<FormFields> name="requesterName" required placeholder="Your name"/>
|
||||
<FormFieldEmail<FormFields> name="requesterEmail" required/>
|
||||
@@ -107,9 +110,11 @@ const PublicTagsSubmitForm = ({ config, userInfo, onSubmitResult }: Props) => {
|
||||
<FormFieldUrl<FormFields> name="companyWebsite" placeholder="Company website"/>
|
||||
{ !isMobile && <div/> }
|
||||
|
||||
<GridItem colSpan={{ base: 1, lg: 3 }} as="h2" textStyle="h4" mt={{ base: 3, lg: 5 }}>
|
||||
Public tags/labels
|
||||
<Hint label="Submit a public tag proposal for our moderation team to review" ml={ 1 } color="link.primary"/>
|
||||
<GridItem colSpan={{ base: 1, lg: 3 }} mt={{ base: 3, lg: 5 }}>
|
||||
<Heading level="2" display="flex" alignItems="center" columnGap={ 1 }>
|
||||
Public tags/labels
|
||||
<Hint label="Submit a public tag proposal for our moderation team to review"/>
|
||||
</Heading>
|
||||
</GridItem>
|
||||
<PublicTagsSubmitFieldAddresses/>
|
||||
<PublicTagsSubmitFieldTags tagTypes={ config?.tagTypes }/>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Box, Flex, Grid, GridItem } from '@chakra-ui/react';
|
||||
import { Flex, Grid, GridItem } from '@chakra-ui/react';
|
||||
import { pickBy } from 'es-toolkit';
|
||||
import React from 'react';
|
||||
|
||||
@@ -8,6 +8,7 @@ import { route } from 'nextjs-routes';
|
||||
|
||||
import { Alert } from 'toolkit/chakra/alert';
|
||||
import { Button } from 'toolkit/chakra/button';
|
||||
import { Heading } from 'toolkit/chakra/heading';
|
||||
import { Link } from 'toolkit/chakra/link';
|
||||
import { makePrettyLink } from 'toolkit/utils/url';
|
||||
|
||||
@@ -43,7 +44,7 @@ const PublicTagsSubmitResult = ({ data }: Props) => {
|
||||
</Alert>
|
||||
) }
|
||||
|
||||
<Box as="h2" textStyle="h4">Company info</Box>
|
||||
<Heading level="2">Company info</Heading>
|
||||
<Grid rowGap={ 3 } columnGap={ 6 } gridTemplateColumns="170px 1fr" mt={ 6 }>
|
||||
<GridItem>Your name</GridItem>
|
||||
<GridItem>{ groupedData.requesterName }</GridItem>
|
||||
@@ -65,7 +66,7 @@ const PublicTagsSubmitResult = ({ data }: Props) => {
|
||||
) }
|
||||
</Grid>
|
||||
|
||||
<Box as="h2" textStyle="h4" mt={ 8 } mb={ 5 }>Public tags/labels</Box>
|
||||
<Heading level="2" mt={ 8 } mb={ 5 }>Public tags/labels</Heading>
|
||||
{ hasErrors ? <PublicTagsSubmitResultWithErrors data={ groupedData }/> : <PublicTagsSubmitResultSuccess data={ groupedData }/> }
|
||||
|
||||
<Flex flexDir={{ base: 'column', lg: 'row' }} columnGap={ 6 } mt={ 8 } rowGap={ 3 }>
|
||||
|
||||
|
Before Width: | Height: | Size: 84 KiB After Width: | Height: | Size: 86 KiB |
|
Before Width: | Height: | Size: 105 KiB After Width: | Height: | Size: 105 KiB |
|
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 56 KiB |
|
Before Width: | Height: | Size: 80 KiB After Width: | Height: | Size: 80 KiB |
|
Before Width: | Height: | Size: 84 KiB After Width: | Height: | Size: 87 KiB |
|
Before Width: | Height: | Size: 78 KiB After Width: | Height: | Size: 78 KiB |
@@ -290,7 +290,7 @@ const SearchResultListItem = ({ data, searchTerm, isLoading, addressFormat }: Pr
|
||||
<Grid templateColumns={ templateCols } alignItems="center" gap={ 2 }>
|
||||
<Skeleton loading={ isLoading } overflow="hidden" display="flex" alignItems="center">
|
||||
<Text whiteSpace="nowrap" overflow="hidden">
|
||||
<HashStringShortenDynamic hash={ hash } isTooltipDisabled/>
|
||||
<HashStringShortenDynamic hash={ hash } noTooltip/>
|
||||
</Text>
|
||||
{ data.is_smart_contract_verified && <IconSvg name="status/success" boxSize="14px" color="green.500" ml={ 1 } flexShrink={ 0 }/> }
|
||||
</Skeleton>
|
||||
|
||||
|
Before Width: | Height: | Size: 7.4 KiB After Width: | Height: | Size: 7.6 KiB |
|
Before Width: | Height: | Size: 7.7 KiB After Width: | Height: | Size: 7.9 KiB |
|
Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 3.4 KiB |
|
Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 3.4 KiB |
@@ -23,9 +23,8 @@ const ButtonItem = ({ className, label, onClick, icon, isDisabled }: Props) => {
|
||||
disabled={ isDisabled }
|
||||
variant="icon_secondary"
|
||||
boxSize={ 8 }
|
||||
_icon={{ boxSize: 6 }}
|
||||
>
|
||||
{ typeof icon === 'string' ? <IconSvg name={ icon }/> : icon }
|
||||
{ typeof icon === 'string' ? <IconSvg name={ icon } boxSize={ 6 }/> : icon }
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
);
|
||||
|
||||
@@ -29,6 +29,7 @@ const ActionBar = ({ children, className, showShadow }: Props) => {
|
||||
className={ className }
|
||||
backgroundColor={{ _light: 'white', _dark: 'black' }}
|
||||
pt={ 6 }
|
||||
mt={ -6 }
|
||||
pb={{ base: 6, lg: 3 }}
|
||||
mx={{ base: -3, lg: 0 }}
|
||||
px={{ base: 3, lg: 0 }}
|
||||
|
||||
@@ -10,10 +10,14 @@ export interface Props extends Omit<IconButtonProps, 'type' | 'loading'> {
|
||||
text: string;
|
||||
type?: 'link' | 'text' | 'share';
|
||||
isLoading?: boolean;
|
||||
// Chakra v3 doesn't support tooltip inside tooltip - https://github.com/chakra-ui/chakra-ui/issues/9939#issuecomment-2817168121
|
||||
// so we disable the copy tooltip manually when the button is inside a tooltip
|
||||
noTooltip?: boolean;
|
||||
tooltipInteractive?: boolean;
|
||||
}
|
||||
|
||||
const CopyToClipboard = (props: Props) => {
|
||||
const { text, type = 'text', isLoading, onClick, boxSize = 5, ...rest } = props;
|
||||
const { text, type = 'text', isLoading, onClick, boxSize = 5, noTooltip, tooltipInteractive, ...rest } = props;
|
||||
|
||||
const { hasCopied, copy, disclosure } = useClipboard(text);
|
||||
|
||||
@@ -23,6 +27,37 @@ const CopyToClipboard = (props: Props) => {
|
||||
onClick?.(event);
|
||||
}, [ onClick, copy ]);
|
||||
|
||||
const iconName = (() => {
|
||||
switch (type) {
|
||||
case 'link':
|
||||
return hasCopied ? 'check' : 'link';
|
||||
case 'share':
|
||||
return hasCopied ? 'check' : 'share';
|
||||
default:
|
||||
return hasCopied ? 'copy_check' : 'copy';
|
||||
}
|
||||
})();
|
||||
|
||||
const button = (
|
||||
<IconButton
|
||||
aria-label="copy"
|
||||
boxSize={ boxSize }
|
||||
onClick={ handleClick }
|
||||
ml={ 2 }
|
||||
borderRadius="sm"
|
||||
loadingSkeleton={ isLoading }
|
||||
variant="icon_secondary"
|
||||
size="2xs"
|
||||
{ ...rest }
|
||||
>
|
||||
<IconSvg name={ iconName }/>
|
||||
</IconButton>
|
||||
);
|
||||
|
||||
if (noTooltip) {
|
||||
return button;
|
||||
}
|
||||
|
||||
const tooltipContent = (() => {
|
||||
if (hasCopied) {
|
||||
return 'Copied';
|
||||
@@ -35,17 +70,6 @@ const CopyToClipboard = (props: Props) => {
|
||||
return 'Copy to clipboard';
|
||||
})();
|
||||
|
||||
const iconName = (() => {
|
||||
switch (type) {
|
||||
case 'link':
|
||||
return 'link';
|
||||
case 'share':
|
||||
return 'share';
|
||||
default:
|
||||
return 'copy';
|
||||
}
|
||||
})();
|
||||
|
||||
return (
|
||||
<Tooltip
|
||||
content={ tooltipContent }
|
||||
@@ -53,20 +77,9 @@ const CopyToClipboard = (props: Props) => {
|
||||
open={ disclosure.open }
|
||||
onOpenChange={ disclosure.onOpenChange }
|
||||
closeOnPointerDown={ false }
|
||||
interactive={ tooltipInteractive }
|
||||
>
|
||||
<IconButton
|
||||
aria-label="copy"
|
||||
boxSize={ boxSize }
|
||||
onClick={ handleClick }
|
||||
ml={ 2 }
|
||||
borderRadius="sm"
|
||||
loadingSkeleton={ isLoading }
|
||||
variant="icon_secondary"
|
||||
size="2xs"
|
||||
{ ...rest }
|
||||
>
|
||||
<IconSvg name={ iconName }/>
|
||||
</IconButton>
|
||||
{ button }
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -6,20 +6,27 @@ import { Tooltip } from 'toolkit/chakra/tooltip';
|
||||
|
||||
interface Props {
|
||||
hash: string;
|
||||
isTooltipDisabled?: boolean;
|
||||
noTooltip?: boolean;
|
||||
tooltipInteractive?: boolean;
|
||||
type?: 'long' | 'short';
|
||||
as?: React.ElementType;
|
||||
}
|
||||
|
||||
const HashStringShorten = ({ hash, isTooltipDisabled, as = 'span', type }: Props) => {
|
||||
const HashStringShorten = ({ hash, noTooltip, as = 'span', type, tooltipInteractive }: Props) => {
|
||||
const charNumber = type === 'long' ? 16 : 8;
|
||||
if (hash.length <= charNumber) {
|
||||
return <chakra.span as={ as }>{ hash }</chakra.span>;
|
||||
}
|
||||
|
||||
const content = <chakra.span as={ as }>{ shortenString(hash, charNumber) }</chakra.span>;
|
||||
|
||||
if (noTooltip) {
|
||||
return content;
|
||||
}
|
||||
|
||||
return (
|
||||
<Tooltip content={ hash } disabled={ isTooltipDisabled }>
|
||||
<chakra.span as={ as }>{ shortenString(hash, charNumber) }</chakra.span>
|
||||
<Tooltip content={ hash } interactive={ tooltipInteractive }>
|
||||
{ content }
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -23,12 +23,13 @@ const HEAD_MIN_LENGTH = 4;
|
||||
interface Props {
|
||||
hash: string;
|
||||
fontWeight?: string | number;
|
||||
isTooltipDisabled?: boolean;
|
||||
noTooltip?: boolean;
|
||||
tooltipInteractive?: boolean;
|
||||
tailLength?: number;
|
||||
as?: React.ElementType;
|
||||
}
|
||||
|
||||
const HashStringShortenDynamic = ({ hash, fontWeight = '400', isTooltipDisabled, tailLength = TAIL_LENGTH, as = 'span' }: Props) => {
|
||||
const HashStringShortenDynamic = ({ hash, fontWeight = '400', noTooltip, tailLength = TAIL_LENGTH, as = 'span', tooltipInteractive }: Props) => {
|
||||
const elementRef = useRef<HTMLSpanElement>(null);
|
||||
const [ displayedString, setDisplayedString ] = React.useState(hash);
|
||||
|
||||
@@ -93,12 +94,12 @@ const HashStringShortenDynamic = ({ hash, fontWeight = '400', isTooltipDisabled,
|
||||
const content = <chakra.span ref={ elementRef } as={ as }>{ displayedString }</chakra.span>;
|
||||
const isTruncated = hash.length !== displayedString.length;
|
||||
|
||||
if (isTruncated) {
|
||||
if (isTruncated && !noTooltip) {
|
||||
return (
|
||||
<Tooltip
|
||||
content={ hash }
|
||||
disabled={ isTooltipDisabled }
|
||||
contentProps={{ maxW: { base: 'calc(100vw - 8px)', lg: '400px' } }}
|
||||
interactive={ tooltipInteractive }
|
||||
>
|
||||
{ content }
|
||||
</Tooltip>
|
||||
|
||||
@@ -10,11 +10,12 @@ interface Props {
|
||||
isLoading?: boolean;
|
||||
value: string;
|
||||
tooltipPlacement?: Placement;
|
||||
tooltipInteractive?: boolean;
|
||||
}
|
||||
|
||||
const TruncatedValue = ({ className, isLoading, value, tooltipPlacement }: Props) => {
|
||||
const TruncatedValue = ({ className, isLoading, value, tooltipPlacement, tooltipInteractive }: Props) => {
|
||||
return (
|
||||
<TruncatedTextTooltip label={ value } placement={ tooltipPlacement }>
|
||||
<TruncatedTextTooltip label={ value } placement={ tooltipPlacement } interactive={ tooltipInteractive }>
|
||||
<Skeleton
|
||||
className={ className }
|
||||
loading={ isLoading }
|
||||
|
||||
@@ -40,7 +40,7 @@ const init = () => {
|
||||
'--w3m-font-family': `${ BODY_TYPEFACE }, sans-serif`,
|
||||
'--w3m-accent': colors.blue[600].value,
|
||||
'--w3m-border-radius-master': '2px',
|
||||
'--w3m-z-index': zIndex?.popover?.value,
|
||||
'--w3m-z-index': zIndex?.modal2?.value,
|
||||
},
|
||||
featuredWalletIds: [],
|
||||
allowUnsupportedChain: true,
|
||||
|
||||
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 26 KiB |
@@ -36,7 +36,9 @@ const Link = chakra((props: LinkProps) => {
|
||||
);
|
||||
});
|
||||
|
||||
type IconProps = Pick<EntityProps, 'address' | 'isSafeAddress'> & EntityBase.IconBaseProps;
|
||||
type IconProps = Pick<EntityProps, 'address' | 'isSafeAddress'> & EntityBase.IconBaseProps & {
|
||||
tooltipInteractive?: boolean;
|
||||
};
|
||||
|
||||
const Icon = (props: IconProps) => {
|
||||
if (props.noIcon) {
|
||||
@@ -70,7 +72,7 @@ const Icon = (props: IconProps) => {
|
||||
const label = (isVerified ? 'verified ' : '') + (isProxy ? 'proxy contract' : 'contract');
|
||||
|
||||
return (
|
||||
<Tooltip content={ label.slice(0, 1).toUpperCase() + label.slice(1) }>
|
||||
<Tooltip content={ label.slice(0, 1).toUpperCase() + label.slice(1) } interactive={ props.tooltipInteractive }>
|
||||
<span>
|
||||
<EntityBase.Icon
|
||||
{ ...props }
|
||||
@@ -90,7 +92,7 @@ const Icon = (props: IconProps) => {
|
||||
})();
|
||||
|
||||
return (
|
||||
<Tooltip content={ label } disabled={ !label }>
|
||||
<Tooltip content={ label } disabled={ !label } interactive={ props.tooltipInteractive }>
|
||||
<Flex marginRight={ styles.marginRight } position="relative">
|
||||
<AddressIdenticon
|
||||
size={ props.variant === 'heading' ? 30 : 20 }
|
||||
@@ -128,7 +130,12 @@ const Content = chakra((props: ContentProps) => {
|
||||
);
|
||||
|
||||
return (
|
||||
<Tooltip content={ label } contentProps={{ maxW: { base: 'calc(100vw - 8px)', lg: '400px' } }}>
|
||||
<Tooltip
|
||||
content={ label }
|
||||
contentProps={{ maxW: { base: 'calc(100vw - 8px)', lg: '400px' } }}
|
||||
triggerProps={{ minW: 0 }}
|
||||
interactive={ props.tooltipInteractive }
|
||||
>
|
||||
<Skeleton loading={ props.isLoading } overflow="hidden" textOverflow="ellipsis" whiteSpace="nowrap" { ...styles }>
|
||||
{ nameText }
|
||||
</Skeleton>
|
||||
@@ -174,7 +181,10 @@ const AddressEntity = (props: EntityProps) => {
|
||||
const settingsContext = useSettingsContext();
|
||||
const altHash = !props.noAltHash && settingsContext?.addressFormat === 'bech32' ? toBech32Address(props.address.hash) : undefined;
|
||||
|
||||
const content = <Content { ...partsProps.content } altHash={ altHash }/>;
|
||||
// inside highlight context all tooltips should be interactive
|
||||
// because non-interactive ones will not pass 'onMouseLeave' event to the parent component
|
||||
// see issue - https://github.com/chakra-ui/chakra-ui/issues/9939#issuecomment-2810567024
|
||||
const content = <Content { ...partsProps.content } altHash={ altHash } tooltipInteractive={ Boolean(highlightContext) }/>;
|
||||
|
||||
return (
|
||||
<Container
|
||||
@@ -187,9 +197,9 @@ const AddressEntity = (props: EntityProps) => {
|
||||
position="relative"
|
||||
zIndex={ 0 }
|
||||
>
|
||||
<Icon { ...partsProps.icon }/>
|
||||
<Icon { ...partsProps.icon } tooltipInteractive={ Boolean(highlightContext) }/>
|
||||
{ props.noLink ? content : <Link { ...partsProps.link }>{ content }</Link> }
|
||||
<Copy { ...partsProps.copy } altHash={ altHash }/>
|
||||
<Copy { ...partsProps.copy } altHash={ altHash } tooltipInteractive={ Boolean(highlightContext) }/>
|
||||
</Container>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -25,7 +25,14 @@ const AddressEntityContentProxy = (props: ContentProps) => {
|
||||
Proxy contract
|
||||
{ props.address.name ? ` (${ props.address.name })` : '' }
|
||||
</Box>
|
||||
<AddressEntity address={{ hash: props.address.hash, filecoin: props.address.filecoin }} noLink noIcon noHighlight justifyContent="center"/>
|
||||
<AddressEntity
|
||||
address={{ hash: props.address.hash, filecoin: props.address.filecoin }}
|
||||
noLink
|
||||
noIcon
|
||||
noHighlight
|
||||
noTooltip
|
||||
justifyContent="center"
|
||||
/>
|
||||
<Box fontWeight={ 600 } mt={ 2 }>
|
||||
Implementation{ implementations.length > 1 ? 's' : '' }
|
||||
{ implementationName ? ` (${ implementationName })` : '' }
|
||||
@@ -38,10 +45,10 @@ const AddressEntityContentProxy = (props: ContentProps) => {
|
||||
noLink
|
||||
noIcon
|
||||
noHighlight
|
||||
noTooltip
|
||||
minW={ `calc((100% - ${ colNum - 1 } * 12px) / ${ colNum })` }
|
||||
flex={ 1 }
|
||||
justifyContent={ colNum === 1 ? 'center' : undefined }
|
||||
isTooltipDisabled
|
||||
/>
|
||||
)) }
|
||||
</Flex>
|
||||
@@ -49,13 +56,13 @@ const AddressEntityContentProxy = (props: ContentProps) => {
|
||||
);
|
||||
|
||||
return (
|
||||
<Tooltip content={ content } interactive contentProps={{ maxW: { base: 'calc(100vw - 8px)', lg: '410px' } }}>
|
||||
<Tooltip content={ content } interactive contentProps={{ maxW: { base: 'calc(100vw - 8px)', lg: '410px' } }} triggerProps={{ minW: 0 }}>
|
||||
<Box display="inline-flex" w="100%">
|
||||
<EntityBase.Content
|
||||
{ ...props }
|
||||
truncation={ nameTag || implementationName || props.address.name ? 'tail' : props.truncation }
|
||||
text={ nameTag || implementationName || props.address.name || props.altHash || props.address.hash }
|
||||
isTooltipDisabled
|
||||
noTooltip
|
||||
/>
|
||||
</Box>
|
||||
</Tooltip>
|
||||
|
||||
@@ -23,7 +23,7 @@ export interface EntityBaseProps {
|
||||
icon?: EntityIconProps;
|
||||
isExternal?: boolean;
|
||||
isLoading?: boolean;
|
||||
isTooltipDisabled?: boolean;
|
||||
noTooltip?: boolean;
|
||||
noCopy?: boolean;
|
||||
noIcon?: boolean;
|
||||
noLink?: boolean;
|
||||
@@ -112,12 +112,23 @@ const Icon = ({ isLoading, noIcon, variant, name, color, borderRadius, marginRig
|
||||
);
|
||||
};
|
||||
|
||||
export interface ContentBaseProps extends Pick<EntityBaseProps, 'className' | 'isLoading' | 'truncation' | 'tailLength' | 'isTooltipDisabled' | 'variant'> {
|
||||
export interface ContentBaseProps extends Pick<EntityBaseProps, 'className' | 'isLoading' | 'truncation' | 'tailLength' | 'noTooltip' | 'variant'> {
|
||||
asProp?: React.ElementType;
|
||||
text: string;
|
||||
tooltipInteractive?: boolean;
|
||||
}
|
||||
|
||||
const Content = chakra(({ className, isLoading, asProp, text, truncation = 'dynamic', tailLength, isTooltipDisabled, variant }: ContentBaseProps) => {
|
||||
const Content = chakra(({
|
||||
className,
|
||||
isLoading,
|
||||
asProp,
|
||||
text,
|
||||
truncation = 'dynamic',
|
||||
tailLength,
|
||||
variant,
|
||||
noTooltip,
|
||||
tooltipInteractive,
|
||||
}: ContentBaseProps) => {
|
||||
const styles = getContentProps(variant);
|
||||
|
||||
if (truncation === 'tail') {
|
||||
@@ -126,6 +137,7 @@ const Content = chakra(({ className, isLoading, asProp, text, truncation = 'dyna
|
||||
className={ className }
|
||||
isLoading={ isLoading }
|
||||
value={ text }
|
||||
tooltipInteractive={ tooltipInteractive }
|
||||
{ ...styles }
|
||||
/>
|
||||
);
|
||||
@@ -139,7 +151,8 @@ const Content = chakra(({ className, isLoading, asProp, text, truncation = 'dyna
|
||||
hash={ text }
|
||||
as={ asProp }
|
||||
type="long"
|
||||
isTooltipDisabled={ isTooltipDisabled }
|
||||
noTooltip={ noTooltip }
|
||||
tooltipInteractive={ tooltipInteractive }
|
||||
/>
|
||||
);
|
||||
case 'constant':
|
||||
@@ -147,7 +160,8 @@ const Content = chakra(({ className, isLoading, asProp, text, truncation = 'dyna
|
||||
<HashStringShorten
|
||||
hash={ text }
|
||||
as={ asProp }
|
||||
isTooltipDisabled={ isTooltipDisabled }
|
||||
noTooltip={ noTooltip }
|
||||
tooltipInteractive={ tooltipInteractive }
|
||||
/>
|
||||
);
|
||||
case 'dynamic':
|
||||
@@ -156,7 +170,8 @@ const Content = chakra(({ className, isLoading, asProp, text, truncation = 'dyna
|
||||
hash={ text }
|
||||
as={ asProp }
|
||||
tailLength={ tailLength }
|
||||
isTooltipDisabled={ isTooltipDisabled }
|
||||
noTooltip={ noTooltip }
|
||||
tooltipInteractive={ tooltipInteractive }
|
||||
/>
|
||||
);
|
||||
case 'none':
|
||||
@@ -178,7 +193,10 @@ const Content = chakra(({ className, isLoading, asProp, text, truncation = 'dyna
|
||||
);
|
||||
});
|
||||
|
||||
export type CopyBaseProps = Pick<CopyToClipboardProps, 'isLoading' | 'text'> & Pick<EntityBaseProps, 'noCopy'>;
|
||||
export type CopyBaseProps =
|
||||
Pick<CopyToClipboardProps, 'isLoading' | 'text' | 'tooltipInteractive'> &
|
||||
Pick<EntityBaseProps, 'noCopy' | 'noTooltip'>
|
||||
;
|
||||
|
||||
const Copy = ({ noCopy, ...props }: CopyBaseProps) => {
|
||||
if (noCopy) {
|
||||
|
||||
|
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 3.4 KiB |
@@ -22,6 +22,12 @@ const FilterButton = ({ isLoading, appliedFiltersNum, ...rest }: Props, ref: Rea
|
||||
size={ 5 }
|
||||
bg={{ _light: 'blue.700', _dark: 'gray.50' }}
|
||||
color={{ _light: 'white', _dark: 'black' }}
|
||||
_groupHover={{
|
||||
bg: 'link.primary.hover',
|
||||
}}
|
||||
_groupExpanded={{
|
||||
bg: 'link.primary.hover',
|
||||
}}
|
||||
>
|
||||
{ appliedFiltersNum }
|
||||
</Circle>
|
||||
|
||||
@@ -14,7 +14,7 @@ interface Props {
|
||||
const PopoverFilter = ({ appliedFiltersNum, children, contentProps, isLoading }: Props) => {
|
||||
return (
|
||||
<PopoverRoot>
|
||||
<PopoverTrigger>
|
||||
<PopoverTrigger className="group">
|
||||
<FilterButton
|
||||
appliedFiltersNum={ appliedFiltersNum }
|
||||
isLoading={ isLoading }
|
||||
|
||||
@@ -21,7 +21,7 @@ const TableColumnFilterWrapper = ({ columnName, className, children, isLoading,
|
||||
<Button
|
||||
display="inline-flex"
|
||||
aria-label={ `filter by ${ columnName }` }
|
||||
variant="dropdown"
|
||||
variant="icon_secondary"
|
||||
borderWidth="0"
|
||||
h="20px"
|
||||
minW="auto"
|
||||
|
||||
|
Before Width: | Height: | Size: 55 KiB After Width: | Height: | Size: 46 KiB |
@@ -224,7 +224,7 @@ const TxInterpretation = ({ summary, isLoading, addressDataMap, className, isNov
|
||||
<Tooltip content="Human readable transaction provided by Noves.fi">
|
||||
<Badge ml={ 2 } verticalAlign="unset" transform="translateY(-2px)">
|
||||
by
|
||||
<Image src={ novesLogoUrl } alt="Noves logo" h="12px" ml={ 1.5 }/>
|
||||
<Image src={ novesLogoUrl } alt="Noves logo" h="12px" ml={ 1.5 } display="inline"/>
|
||||
</Badge>
|
||||
</Tooltip>
|
||||
) }
|
||||
|
||||
@@ -3,7 +3,6 @@ import React from 'react';
|
||||
|
||||
import { TabsContent, TabsList, TabsRoot, TabsTrigger } from 'toolkit/chakra/tabs';
|
||||
import AdaptiveTabs from 'toolkit/components/AdaptiveTabs/AdaptiveTabs';
|
||||
import RoutedTabsSkeleton from 'toolkit/components/RoutedTabs/RoutedTabsSkeleton';
|
||||
|
||||
import { Section, Container, SectionHeader, SamplesStack, Sample, SectionSubHeader } from './parts';
|
||||
|
||||
@@ -74,13 +73,6 @@ const TabsShowcase = () => {
|
||||
/>
|
||||
</Sample>
|
||||
</SamplesStack>
|
||||
|
||||
<SectionSubHeader>Tabs skeleton</SectionSubHeader>
|
||||
<SamplesStack>
|
||||
<Sample>
|
||||
<RoutedTabsSkeleton tabs={ tabs }/>
|
||||
</Sample>
|
||||
</SamplesStack>
|
||||
</Section>
|
||||
</Container>
|
||||
);
|
||||
|
||||
@@ -15,7 +15,7 @@ const TooltipShowcase = () => {
|
||||
<Tooltip content="Tooltip content">
|
||||
<span>Default</span>
|
||||
</Tooltip>
|
||||
<Tooltip content="Tooltip content">
|
||||
<Tooltip content="Tooltip content" interactive>
|
||||
<Utilization value={ 0.5 }/>
|
||||
</Tooltip>
|
||||
</Sample>
|
||||
|
||||
@@ -35,6 +35,7 @@ test('base view', async({ render, page }) => {
|
||||
await expect(page).toHaveScreenshot();
|
||||
|
||||
await page.getByRole('button', { name: 'Network menu' }).click();
|
||||
await page.mouse.move(0, 0);
|
||||
await expect(page).toHaveScreenshot();
|
||||
});
|
||||
|
||||
@@ -48,6 +49,7 @@ test.describe('dark mode', () => {
|
||||
await expect(page).toHaveScreenshot();
|
||||
|
||||
await page.getByRole('button', { name: 'Network menu' }).click();
|
||||
await page.mouse.move(0, 0);
|
||||
await expect(page).toHaveScreenshot();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -58,6 +58,7 @@ const NetworkMenuContentMobile = ({ items, tabs }: Props) => {
|
||||
collection={ selectCollection }
|
||||
placeholder="Select network type"
|
||||
mb={ 3 }
|
||||
contentProps={{ zIndex: 'modal' }}
|
||||
/>
|
||||
) }
|
||||
<VStack as="ul" gap={ 2 } alignItems="stretch">
|
||||
|
||||
@@ -159,6 +159,7 @@ const SearchBar = ({ isHomepage }: Props) => {
|
||||
w={ `${ menuWidth.current }px` }
|
||||
ref={ menuRef }
|
||||
overflow="hidden"
|
||||
zIndex="modal"
|
||||
>
|
||||
<PopoverBody
|
||||
p={ 0 }
|
||||
|
||||
@@ -139,7 +139,7 @@ const SearchBarInput = (
|
||||
position={{ base: isHomepage ? 'static' : 'absolute', lg: 'relative' }}
|
||||
top={{ base: isHomepage ? 0 : 55, lg: 0 }}
|
||||
left="0"
|
||||
zIndex={{ base: isHomepage ? 'auto' : '0', lg: isSuggestOpen ? 'popover' : 'auto' }}
|
||||
zIndex={{ base: isHomepage ? 'auto' : '0', lg: isSuggestOpen ? 'modal' : 'auto' }}
|
||||
paddingX={{ base: isHomepage ? 0 : 3, lg: 0 }}
|
||||
paddingTop={{ base: isHomepage ? 0 : 1, lg: 0 }}
|
||||
paddingBottom={{ base: isHomepage ? 0 : 2, lg: 0 }}
|
||||
|
||||