diff --git a/CHANGELOG.md b/CHANGELOG.md index 301306f27..2d82e551e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## [1.136.2](https://github.com/hirosystems/explorer/compare/v1.136.1...v1.136.2) (2023-12-27) + + +### Bug Fixes + +* api url search param ([d71b374](https://github.com/hirosystems/explorer/commit/d71b374f8cfc16298b34515c5588b3e454de194a)) + ## [1.136.1](https://github.com/hirosystems/explorer/compare/v1.136.0...v1.136.1) (2023-12-27) diff --git a/src/app/__loading.tsx b/src/app/__loading.tsx deleted file mode 100644 index d8dec8a49..000000000 --- a/src/app/__loading.tsx +++ /dev/null @@ -1,38 +0,0 @@ -'use client'; - -import * as React from 'react'; - -import { ExplorerSkeletonLoader } from '../common/components/loaders/skeleton-common'; -import { SkeletonBlockList } from '../common/components/loaders/skeleton-text'; -import { SkeletonTransactionList } from '../common/components/loaders/skeleton-transaction'; -import { TxListTabs } from '../common/components/tx-lists/tabs/TxListTabs'; -import { Grid } from '../ui/Grid'; -import { PageTitle } from './_components/PageTitle'; -import { SkeletonStatSection } from './_components/Stats/SkeletonStatSection'; -import { Wrapper as StatsWrapper } from './_components/Stats/Wrapper'; - -export default function __loading() { - return ( - - - - - - - - - - - } - mempoolList={} - /> - - - ); -} diff --git a/src/app/_components/AppConfig.tsx b/src/app/_components/AppConfig.tsx index a2b48ebfc..98fcec993 100644 --- a/src/app/_components/AppConfig.tsx +++ b/src/app/_components/AppConfig.tsx @@ -7,6 +7,7 @@ import { Connect } from '@stacks/connect-react'; import { useAppSelector } from '../../common/state/hooks'; import { NetworkMode } from '../../common/types/network'; +import { Text } from '../../ui/Text'; import { selectUserSession } from '../sandbox/sandbox-slice'; export const AppConfig: React.FC<{ @@ -18,7 +19,7 @@ export const AppConfig: React.FC<{ const userSession = useAppSelector(selectUserSession); useEffect(() => { toast( -
+ You're viewing {querySubnet ? 'a subnet' : `the ${queryNetworkMode}`} {querySubnet || queryApiUrl ? ( <> @@ -27,7 +28,7 @@ export const AppConfig: React.FC<{ ) : null}{' '} Explorer -
, + , { id: 'network-mode-toast', style: { diff --git a/src/app/_components/BlockList/AnimatedBlockAndMicroblocksItem.tsx b/src/app/_components/BlockList/AnimatedBlockAndMicroblocksItem.tsx index 708d3237f..bbc039ad1 100644 --- a/src/app/_components/BlockList/AnimatedBlockAndMicroblocksItem.tsx +++ b/src/app/_components/BlockList/AnimatedBlockAndMicroblocksItem.tsx @@ -1,12 +1,16 @@ 'use client'; +import { ScaleFade, SlideFade } from '@chakra-ui/react'; import { FC, memo, useEffect, useState } from 'react'; +import { Box } from '../../../ui/Box'; +import { Button } from '../../../ui/Button'; import { Collapse } from '../../../ui/Collapse'; +import { useDisclosure } from '../../../ui/hooks/useDisclosure'; import { BlockAndMicroblocksItem } from './BlockAndMicroblocksItem'; import { EnhancedBlock } from './types'; -export const animationDuration = 0.25; +export const animationDuration = 0.8; export const AnimatedBlockAndMicroblocksItem: FC<{ block: EnhancedBlock; @@ -16,7 +20,9 @@ export const AnimatedBlockAndMicroblocksItem: FC<{ const [show, setShow] = useState(!block.animate); useEffect(() => { if (block.animate) { - setShow(true); + setTimeout(() => { + setShow(true); + }, 100); } }, [block.animate]); useEffect(() => { @@ -26,22 +32,26 @@ export const AnimatedBlockAndMicroblocksItem: FC<{ }, [block.destroy]); return ( - { - if (state === 'exit') { - onAnimationExit?.(); - } - }} - data-testid={`block-item-${block.hash}`} - > - - + <> + + { + if (state === 'exit') { + onAnimationExit?.(); + } + }} + data-testid={`block-item-${block.hash}`} + > + + + + ); }, (prevProps, currentProps) => diff --git a/src/app/_components/BlockList/BlockAndMicroblocksItem.tsx b/src/app/_components/BlockList/BlockAndMicroblocksItem.tsx index b5511ea42..da484c32d 100644 --- a/src/app/_components/BlockList/BlockAndMicroblocksItem.tsx +++ b/src/app/_components/BlockList/BlockAndMicroblocksItem.tsx @@ -2,7 +2,6 @@ import React, { memo } from 'react'; import { Block } from '@stacks/stacks-blockchain-api-types'; -import { useVerticallyStackedElementsBorderStyle } from '../../../common/hooks/useVerticallyStackedElementsBorderStyle'; import { AccordionButton } from '../../../ui/AccordionButton'; import { AccordionIcon } from '../../../ui/AccordionIcon'; import { AccordionItem } from '../../../ui/AccordionItem'; @@ -15,7 +14,11 @@ import { MicroblockListItem } from './MicroblockListItem'; export const BlockAndMicroblocksItem: React.FC<{ block: Block }> = memo( ({ block }) => { return ( - + = memo( - + {!!block.microblocks_accepted?.length ? ( block.microblocks_accepted.map((microblockHash, microblockIndex) => ( = React.memo( ({ block, ...rest }) => { return ( - - - - - } leftContent={{ title: ( { e.stopPropagation(); }} - color={'textTitle'} alignItems="center" > = React.memo( ), subtitle: ( - + {addSepBetweenStrings([ `${block?.microblocks_accepted?.length || 0} ${pluralize( 'microblock', @@ -57,19 +45,8 @@ export const BlockListItem: React.FC<{ block: Block } & FlexProps> = React.memo( ), }} rightContent={{ - title: ( - - {toRelativeTime(block.burn_block_time * 1000)} - - ), - subtitle: {truncateMiddle(block.hash)}, + title: toRelativeTime(block.burn_block_time * 1000), + subtitle: truncateMiddle(block.hash), }} {...rest} /> diff --git a/src/app/_components/BlockList/BlockListWithControls.tsx b/src/app/_components/BlockList/BlockListWithControls.tsx index 6802db31a..271f99b1d 100644 --- a/src/app/_components/BlockList/BlockListWithControls.tsx +++ b/src/app/_components/BlockList/BlockListWithControls.tsx @@ -50,7 +50,7 @@ export function BlockListWithControls() { + { setLoading(true); @@ -187,20 +187,9 @@ export function BlockListWithControls() { } footer={ - <> - - + } > ); diff --git a/src/app/_components/BlockList/MicroblockListItem.tsx b/src/app/_components/BlockList/MicroblockListItem.tsx index 83a40cbf8..3a9e7c700 100644 --- a/src/app/_components/BlockList/MicroblockListItem.tsx +++ b/src/app/_components/BlockList/MicroblockListItem.tsx @@ -2,10 +2,11 @@ import { useColorMode } from '@chakra-ui/react'; import React from 'react'; import { FiZap } from 'react-icons/fi'; +import { Circle } from '../../../common/components/Circle'; import { MicroBlockLink } from '../../../common/components/ExplorerLinks'; import { toRelativeTime } from '../../../common/utils/utils'; -import { Circle } from '../../../ui/Circle'; import { Flex } from '../../../ui/Flex'; +import { HStack } from '../../../ui/HStack'; import { Icon } from '../../../ui/Icon'; import { Stack } from '../../../ui/Stack'; import { Caption, Text, Title } from '../../../ui/typography'; @@ -19,28 +20,21 @@ export const MicroblockListItem: React.FC<{ - + - + <Caption display="block">Microblock</Caption> </Stack> - </Stack> - <Stack spacing="8px" textAlign="right" as="span" minWidth={'80px'}> + </HStack> + <Stack gap={2} textAlign="right" as="span" minWidth={'80px'}> <Text fontSize="14px" width="100%" diff --git a/src/app/_components/BlockList/SkeletonBlockList.tsx b/src/app/_components/BlockList/SkeletonBlockList.tsx new file mode 100644 index 000000000..2e6534f9a --- /dev/null +++ b/src/app/_components/BlockList/SkeletonBlockList.tsx @@ -0,0 +1,20 @@ +import * as React from 'react'; + +import { Section } from '../../../common/components/Section'; +import { TwoColumnsListItemSkeleton } from '../../../common/components/TwoColumnsListItemSkeleton'; + +export const SkeletonBlockList = () => { + return ( + <Section title="Recent Blocks"> + {[...Array(10)].map((_, i) => ( + <TwoColumnsListItemSkeleton + key={i} + leftContentSubtitle + leftContentTitle + rightContentSubtitle + rightContentTitle + /> + ))} + </Section> + ); +}; diff --git a/src/app/_components/BlockList/__tests__/BlocksListBase.test.tsx b/src/app/_components/BlockList/__tests__/BlocksListBase.test.tsx index 542aa26fa..db5f6d8d5 100644 --- a/src/app/_components/BlockList/__tests__/BlocksListBase.test.tsx +++ b/src/app/_components/BlockList/__tests__/BlocksListBase.test.tsx @@ -1,4 +1,3 @@ -import { hash } from '@noble/hashes/_assert'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { act, fireEvent, render, screen, waitFor } from '@testing-library/react'; import React from 'react'; @@ -10,6 +9,7 @@ import { import { Block } from '@stacks/stacks-blockchain-api-types'; import { useSuspenseBlockListInfinite as useSuspenseBlockListInfiniteActual } from '../../../../common/queries/useBlockListInfinite'; +import '../../../../common/utils/test-utils/matchMedia.mock'; import { BlocksList } from '../index'; import { EnhancedBlock } from '../types'; diff --git a/src/app/_components/BlockList/__tests__/__snapshots__/MicroblockListItem.test.tsx.snap b/src/app/_components/BlockList/__tests__/__snapshots__/MicroblockListItem.test.tsx.snap index df2e6590e..2d56a98fe 100644 --- a/src/app/_components/BlockList/__tests__/__snapshots__/MicroblockListItem.test.tsx.snap +++ b/src/app/_components/BlockList/__tests__/__snapshots__/MicroblockListItem.test.tsx.snap @@ -3,13 +3,13 @@ exports[`MicroblockListItem component renders correctly 1`] = ` <DocumentFragment> <div - class="css-brzhns" + class="css-1qnwiy1" > <span - class="chakra-stack css-1h7m9p5" + class="chakra-stack css-19ovbli" > <div - class="css-1gqk193" + class="css-197es7z" > <svg class="chakra-icon css-1bc6rbx" @@ -30,20 +30,24 @@ exports[`MicroblockListItem component renders correctly 1`] = ` </svg> </div> <span - class="chakra-stack css-x6ucgl" + class="chakra-stack css-on31qd" > <div - class="css-1exw3s2" + class="css-ycwyyy" > <a - class="chakra-text css-jqqks0" + class="chakra-link css-0" href="/microblock/mockHash?chain=mainnet" > - mockHash + <span + class="chakra-text css-10wgqlb" + > + mockHash + </span> </a> </div> <span - class="chakra-text css-1h2w9sn" + class="chakra-text css-15iimks" style="user-select: none;" > Microblock @@ -51,10 +55,10 @@ exports[`MicroblockListItem component renders correctly 1`] = ` </span> </span> <span - class="chakra-stack css-afdpdg" + class="chakra-stack css-fdzsh0" > <span - class="chakra-text css-zousv3" + class="chakra-text css-ui7t6h" > MockRelativeTime1619047871000 </span> diff --git a/src/app/_components/BlockList/index.tsx b/src/app/_components/BlockList/index.tsx index f5bf4afa5..08259322f 100644 --- a/src/app/_components/BlockList/index.tsx +++ b/src/app/_components/BlockList/index.tsx @@ -1,19 +1,22 @@ 'use client'; +import { useColorModeValue } from '@chakra-ui/react'; import { useQueryClient } from '@tanstack/react-query'; import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { connectWebSocketClient } from '@stacks/blockchain-api-client'; import { Block } from '@stacks/stacks-blockchain-api-types'; +import { ListFooter } from '../../../common/components/ListFooter'; import { Section } from '../../../common/components/Section'; -import { SectionFooterActions } from '../../../common/components/SectionFooterActions'; import { SkeletonBlockList } from '../../../common/components/loaders/skeleton-text'; import { DEFAULT_LIST_LIMIT } from '../../../common/constants/constants'; import { useGlobalContext } from '../../../common/context/useAppContext'; import { useSuspenseInfiniteQueryResult } from '../../../common/hooks/useInfiniteQueryResult'; import { useSuspenseBlockListInfinite } from '../../../common/queries/useBlockListInfinite'; import { Accordion } from '../../../ui/Accordion'; +import { Box } from '../../../ui/Box'; +import { FlexProps } from '../../../ui/Flex'; import { FormControl } from '../../../ui/FormControl'; import { FormLabel } from '../../../ui/FormLabel'; import { Switch } from '../../../ui/Switch'; @@ -22,9 +25,11 @@ import { AnimatedBlockAndMicroblocksItem } from './AnimatedBlockAndMicroblocksIt import { BlockAndMicroblocksItem } from './BlockAndMicroblocksItem'; import { EnhancedBlock } from './types'; -const BlocksListBase: React.FC<{ +function BlocksListBase({ + limit, +}: { limit?: number; -}> = ({ limit }) => { +} & FlexProps) { const [isLive, setIsLive] = React.useState(false); const [initialBlocks, setInitialBlocks] = useState<EnhancedBlock[]>([]); const [latestBlocks, setLatestBlocks] = useState<EnhancedBlock[]>([]); @@ -35,13 +40,15 @@ const BlocksListBase: React.FC<{ const blocks = useSuspenseInfiniteQueryResult<Block>(response, limit); + const labelColor = useColorModeValue('slate.600', 'slate.400'); + useEffect(() => { setInitialBlocks(blocks); }, [blocks]); useEffect(() => { if (!isLive) return; - queryClient.invalidateQueries({ queryKey: ['blockListInfinite'] }); + void queryClient.invalidateQueries({ queryKey: ['blockListInfinite'] }); let sub: { unsubscribe?: () => Promise<void>; }; @@ -88,40 +95,48 @@ const BlocksListBase: React.FC<{ gridColumnStart={['1', '1', '2']} gridColumnEnd={['2', '2', '3']} minWidth={0} + flexGrow={0} + flexShrink={1} topRight={ <FormControl display="flex" alignItems="center"> - <FormLabel htmlFor="blocks-live-view-switch" mb="0"> + <FormLabel htmlFor="blocks-live-view-switch" mb="0" color={labelColor}> live view </FormLabel> - <Switch id="blocks-live-view-switch" onChange={() => setIsLive(!isLive)} /> + <Switch + id="blocks-live-view-switch" + isChecked={isLive} + onChange={() => setIsLive(!isLive)} + /> </FormControl> } > - <Accordion allowMultiple> - {allBlocks?.map(block => - isLive ? ( - <AnimatedBlockAndMicroblocksItem - block={block} - key={block.hash} - onAnimationExit={() => removeOldBlock(block)} - /> - ) : ( - <BlockAndMicroblocksItem block={block} key={block.hash} /> - ) + <Box pb={6}> + <Accordion allowMultiple> + {allBlocks?.map(block => + isLive ? ( + <AnimatedBlockAndMicroblocksItem + block={block} + key={block.hash} + onAnimationExit={() => removeOldBlock(block)} + /> + ) : ( + <BlockAndMicroblocksItem block={block} key={block.hash} /> + ) + )} + </Accordion> + {!isLive && ( + <ListFooter + isLoading={isFetchingNextPage} + hasNextPage={hasNextPage} + href={limit ? '/blocks' : undefined} + fetchNextPage={limit ? undefined : fetchNextPage} + label={'blocks'} + /> )} - </Accordion> - {!isLive && ( - <SectionFooterActions - isLoading={isFetchingNextPage} - hasNextPage={hasNextPage} - href={limit ? '/blocks' : undefined} - fetchNextPage={limit ? undefined : fetchNextPage} - label={'blocks'} - /> - )} + </Box> </Section> ); -}; +} export function BlocksList({ limit }: { limit?: number }) { return ( diff --git a/src/app/_components/BtcAnchorBlockCard.tsx b/src/app/_components/BtcAnchorBlockCard.tsx index 053a3fe49..f5d68d069 100644 --- a/src/app/_components/BtcAnchorBlockCard.tsx +++ b/src/app/_components/BtcAnchorBlockCard.tsx @@ -5,7 +5,6 @@ import { Block } from '@stacks/stacks-blockchain-api-types'; import { KeyValueVertical } from '../../common/components/KeyValueVertical'; import { Section } from '../../common/components/Section'; import { useGlobalContext } from '../../common/context/useAppContext'; -import { useVerticallyStackedElementsBorderStyle } from '../../common/hooks/useVerticallyStackedElementsBorderStyle'; import { truncateMiddle } from '../../common/utils/utils'; import { Box } from '../../ui/Box'; import { Text } from '../../ui/Text'; @@ -18,49 +17,47 @@ export function BtcAnchorBlockCardBase({ block }: { block?: Block }) { return ( <Section title="Bitcoin anchor"> - <Box px="16px" css={useVerticallyStackedElementsBorderStyle}> - <KeyValueVertical - label={'Bitcoin block height'} - value={ - <TextLink as="a" target="_blank" href={`${btcBlockBaseUrl}/${block.burn_block_height}`}> - <Text fontSize={'14px'} fontWeight={500}> - #{block.burn_block_height} - </Text> - </TextLink> - } - copyValue={block.burn_block_height.toString()} - /> - <KeyValueVertical - label={'Bitcoin block hash'} - value={ - <TextLink - as="a" - target="_blank" - href={`${btcBlockBaseUrl}/${block.burn_block_hash.replace('0x', '')}`} - > - <Text fontSize={'14px'} fontWeight={500}> - {truncateMiddle(block.burn_block_hash, 8)} - </Text> - </TextLink> - } - copyValue={block.burn_block_hash} - /> - <KeyValueVertical - label={'Anchor transaction ID'} - value={ - <TextLink - as="a" - target="_blank" - href={`${btcTxBaseUrl}/${block.miner_txid.replace('0x', '')}`} - > - <Text fontSize={'14px'} fontWeight={500}> - {truncateMiddle(block.miner_txid, 8)} - </Text> - </TextLink> - } - copyValue={block.miner_txid} - /> - </Box> + <KeyValueVertical + label={'Bitcoin block height'} + value={ + <TextLink as="a" target="_blank" href={`${btcBlockBaseUrl}/${block.burn_block_height}`}> + <Text fontSize={'14px'} fontWeight={'medium'}> + #{block.burn_block_height} + </Text> + </TextLink> + } + copyValue={block.burn_block_height.toString()} + /> + <KeyValueVertical + label={'Bitcoin block hash'} + value={ + <TextLink + as="a" + target="_blank" + href={`${btcBlockBaseUrl}/${block.burn_block_hash.replace('0x', '')}`} + > + <Text fontSize={'14px'} fontWeight={'medium'}> + {truncateMiddle(block.burn_block_hash, 8)} + </Text> + </TextLink> + } + copyValue={block.burn_block_hash} + /> + <KeyValueVertical + label={'Anchor transaction ID'} + value={ + <TextLink + as="a" + target="_blank" + href={`${btcTxBaseUrl}/${block.miner_txid.replace('0x', '')}`} + > + <Text fontSize={'14px'} fontWeight={'medium'}> + {truncateMiddle(block.miner_txid, 8)} + </Text> + </TextLink> + } + copyValue={block.miner_txid} + /> </Section> ); } diff --git a/src/app/_components/ErrorBox.tsx b/src/app/_components/ErrorBox.tsx index 47dab5f68..6718b3e97 100644 --- a/src/app/_components/ErrorBox.tsx +++ b/src/app/_components/ErrorBox.tsx @@ -25,7 +25,6 @@ export function ErrorBox({ const network = useGlobalContext().activeNetwork; const errorName = error.name || 'Unknown error'; const errorMessage = error.message || 'Something went wrong, please try again later.'; - const colorMode = useColorMode().colorMode; return ( <Flex direction={'column'} @@ -37,12 +36,12 @@ export function ErrorBox({ padding="10px" {...flexProps} > - <Icon as={VscError} size="24px" color={'gray.700'} /> + <Icon as={VscError} size="24px" /> <Flex direction={'column'} alignItems={'center'} gap={'4px'}> - <Text color={`feedbackError.${colorMode}`} fontSize={14}> + <Text color={`error`} fontSize={14}> {errorName} </Text> - <Text fontSize={12} color={'gray.700'}> + <Text fontSize={12} textAlign={'center'}> {errorMessage} </Text> </Flex> diff --git a/src/app/_components/Footer.tsx b/src/app/_components/Footer.tsx index 5188b9289..d5e221223 100644 --- a/src/app/_components/Footer.tsx +++ b/src/app/_components/Footer.tsx @@ -1,7 +1,6 @@ 'use client'; -import Link, { LinkProps } from 'next/link'; -import React, { FC, HTMLProps } from 'react'; +import React, { FC } from 'react'; import { PAGE_MAX_WIDTH } from '../../common/constants/constants'; import { RELEASE_TAG_NAME } from '../../common/constants/env'; @@ -9,28 +8,12 @@ import { useGlobalContext } from '../../common/context/useAppContext'; import { buildUrl } from '../../common/utils/buildUrl'; import { Box } from '../../ui/Box'; import { Flex } from '../../ui/Flex'; -import { TextLink } from '../../ui/TextLink'; +import { Link, LinkProps } from '../../ui/Link'; -const FooterLink: FC<LinkProps & HTMLProps<HTMLAnchorElement>> = ({ - children, - href, - target, - rel, -}) => { +const FooterLink: FC<LinkProps> = ({ children, href, target, rel }) => { return ( - <Link href={href} passHref legacyBehavior> - <TextLink - cursor="pointer" - textStyle="body.small" - color={'textCaption'} - textDecoration="none" - fontSize={'14px'} - _hover={{ textDecoration: 'underline' }} - target={target} - rel={rel} - > - {children} - </TextLink> + <Link href={href} fontSize={'xs'} target={target} rel={rel}> + {children} </Link> ); }; diff --git a/src/app/_components/GlobalStyles.tsx b/src/app/_components/GlobalStyles.tsx deleted file mode 100644 index 8493ccb53..000000000 --- a/src/app/_components/GlobalStyles.tsx +++ /dev/null @@ -1,339 +0,0 @@ -'use client'; - -import { Global, css } from '@emotion/react'; -import * as React from 'react'; - -const TextAreaOverrides = ( - <Global - key={'textarea-overrides'} - styles={css` - .clarity-editor-wrapper { - position: absolute !important; - } - - .code-editor { - pre { - width: fit-content; - min-width: 100%; - transform: translateY(2px); - } - - input, - textarea, - [contenteditable] { - caret-color: white; - } - - & * { - font-size: 14px !important; - } - - textarea { - width: 100% !important; - padding-left: 76px !important; - font-size: 14px !important; - padding-top: 2px !important; - font-family: 'Fira Code', monospace !important; - line-height: 24px !important; - outline: transparent; - } - - & > div { - overflow: initial !important; - } - - textarea, - pre { - white-space: pre !important; - overflow-wrap: unset !important; - } - } - `} - /> -); - -export const PrismTheme = ( - <Global - key={'prism-theme'} - styles={css` - code[class*='language-'], - pre[class*='language-'] { - color: #c5c8c6 !important; - } - - .token.comment, - .token.prolog, - .token.doctype, - .token.cdata { - color: #c5c8c6 !important; - opacity: 0.85; - } - - .token.punctuation { - color: #c5c8c6 !important; - } - - .namespace { - opacity: 0.7; - } - - .token.property, - .token.keyword, - .token.tag { - color: #96cbfe !important; - } - - .token.class-name { - color: #ffffb6 !important; - text-decoration: underline; - } - - .token.boolean, - .token.constant { - color: #99cc99 !important; - } - - .token.symbol, - .token.deleted { - color: #f92672 !important; - } - - .token.number { - color: #ff73fd !important; - } - - .token.selector, - .token.attr-name, - .token.string, - .token.char, - .token.builtin, - .token.inserted { - color: #a8ff60 !important; - } - - .token.variable { - color: #c6c5fe !important; - } - - .token.operator { - color: #ededed !important; - } - - .token.entity { - color: #ffffb6 !important; - cursor: help; - } - - .token.url { - color: #96cbfe !important; - } - - .language-css .token.string, - .style .token.string { - color: #87c38a !important; - } - - .token.atrule, - .token.attr-value { - color: #f9ee98 !important; - } - - .token.function { - color: #dad085 !important; - } - - .token.regex { - color: #e9c062 !important; - } - - .token.important { - color: #fd971f !important; - } - - .token.important, - .token.bold { - font-weight: bold; - } - - .token.italic { - font-style: italic; - } - `} - /> -); - -const globalStyles = css` - html, - body, - #__next { - height: 100%; - } - - .prism-code *::selection { - background-color: #aab3ff; - color: white !important; - } - - .portal { - position: relative; - } - - a { - text-decoration: none; - } - - /* cyrillic-ext */ - @font-face { - font-family: 'Fira Code'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(https://fonts.gstatic.com/s/firacode/v9/uU9eCBsR6Z2vfE9aq3bL0fxyUs4tcw4W_D1sJV37MOzlojwUKaJO.woff) - format('woff'); - unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; - } - /* cyrillic */ - @font-face { - font-family: 'Fira Code'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(https://fonts.gstatic.com/s/firacode/v9/uU9eCBsR6Z2vfE9aq3bL0fxyUs4tcw4W_D1sJVT7MOzlojwUKaJO.woff) - format('woff'); - unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; - } - /* greek-ext */ - @font-face { - font-family: 'Fira Code'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(https://fonts.gstatic.com/s/firacode/v9/uU9eCBsR6Z2vfE9aq3bL0fxyUs4tcw4W_D1sJVz7MOzlojwUKaJO.woff) - format('woff'); - unicode-range: U+1F00-1FFF; - } - /* greek */ - @font-face { - font-family: 'Fira Code'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(https://fonts.gstatic.com/s/firacode/v9/uU9eCBsR6Z2vfE9aq3bL0fxyUs4tcw4W_D1sJVP7MOzlojwUKaJO.woff) - format('woff'); - unicode-range: U+0370-03FF; - } - /* latin-ext */ - @font-face { - font-family: 'Fira Code'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(https://fonts.gstatic.com/s/firacode/v9/uU9eCBsR6Z2vfE9aq3bL0fxyUs4tcw4W_D1sJV77MOzlojwUKaJO.woff) - format('woff'); - unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, - U+2C60-2C7F, U+A720-A7FF; - } - /* latin */ - @font-face { - font-family: 'Fira Code'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(https://fonts.gstatic.com/s/firacode/v9/uU9eCBsR6Z2vfE9aq3bL0fxyUs4tcw4W_D1sJVD7MOzlojwUKQ.woff) - format('woff'); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, - U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; - } - - @font-face { - font-family: 'Open Sauce'; - src: - url('/fonts/opensaucesans-medium-webfont.woff2') format('woff2'), - url('/fonts/opensaucesans-medium-webfont.woff') format('woff'); - font-weight: 500; - font-display: swap; - font-style: normal; - } - - @font-face { - font-family: 'Open Sauce'; - src: - url('/fonts/opensaucesans-regular-webfont.woff2') format('woff2'), - url('/fonts/opensaucesans-regular-webfont.woff') format('woff'); - font-weight: 400; - font-weight: normal; - font-style: normal; - } - - .skeleton-outer { - display: flex; - align-items: center; - } - - .skeleton-outer-full-width { - display: flex; - width: 100%; - align-items: center; - } - - .skeleton-title { - --base-color: rgba(255, 255, 255, 0.24); - --highlight-color: rgba(255, 255, 255, 0.74); - } - - .dark .react-loading-skeleton { - --base-color: #1a1a1a; - } - - .dark .react-loading-skeleton::after { - --base-color: #1a1a1a; - --highlight-color: #151515; - } - - .dark .skeleton-title { - --base-color: rgba(255, 255, 255, 0.24); - } - - .dark.skeleton-title::after { - --base-color: rgba(255, 255, 255, 0.24); - --highlight-color: rgba(255, 255, 255, 0.74); - } - - @keyframes react-loading-skeleton { - 100% { - transform: translateX(100%); - } - } - - .ssr-skeleton { - background-color: #d9d9d9; - width: 100%; - border-radius: 0.25rem; - display: inline-flex; - line-height: 1; - position: relative; - overflow: hidden; - } - - .ssr-skeleton:after { - content: ' '; - display: block; - position: absolute; - top: 0; - left: 0; - right: 0; - height: 100%; - background-repeat: no-repeat; - background-image: linear-gradient(90deg, #d9d9d9, #f5f5f5, #d9d9d9); - transform: translateX(-100%); - animation-name: react-loading-skeleton; - animation-direction: normal; - animation-duration: 1.5s; - animation-timing-function: ease-in-out; - animation-iteration-count: infinite; - } -`; - -const GlobalStyles = <Global styles={globalStyles} key={'global-styles'} />; - -export { TextAreaOverrides, GlobalStyles }; diff --git a/src/app/_components/NavBar/BtcStxPrice.tsx b/src/app/_components/NavBar/BtcStxPrice.tsx index 8457d464c..91956c845 100644 --- a/src/app/_components/NavBar/BtcStxPrice.tsx +++ b/src/app/_components/NavBar/BtcStxPrice.tsx @@ -1,41 +1,34 @@ 'use client'; -import { css } from '@emotion/react'; import * as React from 'react'; -import { Fragment } from 'react'; +import { ReactNode } from 'react'; import { Circle } from '../../../common/components/Circle'; -import { ExplorerSkeletonLoader } from '../../../common/components/loaders/skeleton-common'; import { useCurrentBtcPrice, useSuspenseCurrentStxPrice, } from '../../../common/queries/useCurrentPrices'; import { usdFormatter } from '../../../common/utils/utils'; -import { Box } from '../../../ui/Box'; -import { Flex } from '../../../ui/Flex'; +import { Flex, FlexProps } from '../../../ui/Flex'; import { Icon } from '../../../ui/Icon'; +import { Skeleton } from '../../../ui/Skeleton'; import { BitcoinIcon, StxIcon } from '../../../ui/icons'; import { ExplorerErrorBoundary } from '../ErrorBoundary'; -const wrapperStyle = css` - display: flex; - color: #fff; - gap: 5px; - align-items: center; -`; - -const iconStyle = css` - position: relative; - height: 18px; - width: 18px; - border-radius: 18px; - svg { - position: absolute; - left: 50%; - top: 50%; - transform: translate(-50%, -50%); - } -`; +function PriceContainer({ + icon, + children, + ...rest +}: { icon: ReactNode; children: ReactNode } & FlexProps) { + return ( + <Flex color={'slate.50'} gap={1.5} alignItems={'center'} {...rest}> + {icon} + <Flex fontWeight={'medium'} fontSize={'xs'} flexGrow={1} suppressHydrationWarning={true}> + {children} + </Flex> + </Flex> + ); +} function BtcStxPriceBase() { const { @@ -51,48 +44,33 @@ function BtcStxPriceBase() { const formattedBtcPrice = btcPrice ? usdFormatter.format(btcPrice) : ''; const formattedStxPrice = stxPrice ? usdFormatter.format(stxPrice) : ''; return ( - <Fragment> - <Box css={wrapperStyle}> - <Circle size={18} bg="white" css={iconStyle}> - <Icon as={BitcoinIcon} color={'#f7931a'} size={19} /> - </Circle> - <Flex - fontWeight={500} - fontSize={'14px'} - alignItems={'center'} - minWidth={'65px'} - suppressHydrationWarning={true} - > - {isBtcPriceError ? ( - 'N/A' - ) : isBtcPriceFetching || false ? ( - <ExplorerSkeletonLoader width={'70px'} height={'15px'} /> - ) : ( - formattedBtcPrice - )} - </Flex> - </Box> - <Box css={wrapperStyle}> - <Circle size={18} bg="accent.light" css={iconStyle}> - <Icon as={StxIcon} strokeWidth={2} size="11px" color="white" /> - </Circle> - <Flex - fontWeight={500} - fontSize={'14px'} - alignItems={'center'} - minWidth={'35px'} - suppressHydrationWarning={true} - > - {isStxPriceError ? ( - 'N/A' - ) : isStxPriceFetching ? ( - <ExplorerSkeletonLoader width={'35px'} height={'15px'} /> - ) : ( - formattedStxPrice - )} - </Flex> - </Box> - </Fragment> + <Flex gap={6} minWidth={'172px'}> + <PriceContainer icon={<Icon as={BitcoinIcon} size={4.5} />} minWidth={'92px'}> + {isBtcPriceError || !formattedBtcPrice ? ( + 'N/A' + ) : isBtcPriceFetching ? ( + <Skeleton display={'flex'} flexGrow={1} height={3} /> + ) : ( + formattedBtcPrice + )} + </PriceContainer> + <PriceContainer + icon={ + <Circle size={4.5} bg="brand" border={'none'}> + <Icon as={StxIcon} size={2.5} color="white" /> + </Circle> + } + minWidth={'56px'} + > + {isStxPriceError || !formattedStxPrice ? ( + 'N/A' + ) : isStxPriceFetching ? ( + <Skeleton display={'flex'} flexGrow={1} height={3} /> + ) : ( + formattedStxPrice + )} + </PriceContainer> + </Flex> ); } diff --git a/src/app/_components/NavBar/ColorModeButton.tsx b/src/app/_components/NavBar/ColorModeButton.tsx index e41fc71c9..c9b9e5759 100644 --- a/src/app/_components/NavBar/ColorModeButton.tsx +++ b/src/app/_components/NavBar/ColorModeButton.tsx @@ -2,13 +2,14 @@ import { useColorMode } from '@chakra-ui/react'; import { FC } from 'react'; +import { PiMoon, PiSunDim } from 'react-icons/pi'; import { TbSun, TbSunOff } from 'react-icons/tb'; import { IconButton, IconButtonProps } from '../../../ui/IconButton'; export const ColorModeButton: FC<IconButtonProps> = props => { const { colorMode, toggleColorMode } = useColorMode(); - const Icon = colorMode === 'light' ? TbSun : TbSunOff; + const Icon = colorMode === 'light' ? PiSunDim : PiMoon; return ( <IconButton icon={<Icon />} diff --git a/src/app/_components/NavBar/DesktopNav.tsx b/src/app/_components/NavBar/DesktopNav.tsx index 96c27785b..de32488fc 100644 --- a/src/app/_components/NavBar/DesktopNav.tsx +++ b/src/app/_components/NavBar/DesktopNav.tsx @@ -1,60 +1,54 @@ -import { useColorMode } from '@chakra-ui/react'; import { FC } from 'react'; -import { BsChevronDown } from 'react-icons/bs'; +import { PiCaretDown } from 'react-icons/pi'; -import { Box } from '../../../ui/Box'; import { Flex } from '../../../ui/Flex'; import { Icon } from '../../../ui/Icon'; +import { Link } from '../../../ui/Link'; import { Popover } from '../../../ui/Popover'; import { PopoverContent } from '../../../ui/PopoverContent'; import { PopoverTrigger } from '../../../ui/PopoverTrigger'; -import { Stack } from '../../../ui/Stack'; +import { Text } from '../../../ui/Text'; import { DesktopSubNav } from './DesktopSubNav'; import { NavItem } from './types'; export const DesktopNav: FC<{ navItems: NavItem[] }> = ({ navItems }) => { - const colorMode = useColorMode().colorMode; return ( - <Stack direction={'row'} spacing={4}> + <Flex gap={6}> {navItems.map(navItem => ( <Flex key={navItem.id} alignItems={'center'}> <Popover trigger={'hover'} placement={'bottom-start'} isLazy> <PopoverTrigger> - <Flex alignItems={'center'} gap={'2px'}> - <Box - as="a" + <Flex + gap={1.5} + border={ + navItem.id === 'network' + ? '1px solid var(--stacks-colors-whiteAlpha-600)' + : undefined + } + bg={navItem.id === 'network' ? 'whiteAlpha.200' : undefined} + px={navItem.id === 'network' ? 3 : undefined} + py={navItem.id === 'network' ? 1.5 : undefined} + rounded={navItem.id === 'network' ? 'full' : undefined} + > + <Link href={navItem.href ?? '#'} - color={'#fff'} - fontSize="14px" - fontWeight={500} + color={'slate.50'} + fontSize="xs" + fontWeight={'medium'} _hover={{ textDecoration: navItem.children ? 'none' : 'underline', }} > {navItem.label} - </Box> - {navItem.children && ( - <Icon - as={BsChevronDown} - transition={'all .25s ease-in-out'} - transform={false ? 'rotate(180deg)' : ''} - size={3} - color={'#fff'} - position={'relative'} - top={'2px'} - /> - )} + </Link> + {navItem.children && <Icon as={PiCaretDown} size={3.5} color={'white'} />} </Flex> </PopoverTrigger> {navItem.children && ( - <PopoverContent boxShadow={'xl'} bg={`bg.${colorMode}`} rounded={'xl'} mt={'16px'}> + <PopoverContent boxShadow={'xl'} bg={`white`} rounded={'xl'} mt={4}> <Flex direction={'column'}> {navItem.children.map((child, index) => ( - <DesktopSubNav - key={child.id} - hasDivider={index < (navItem.children?.length || 0) - 1} - {...child} - /> + <DesktopSubNav key={child.id} {...child} /> ))} </Flex> </PopoverContent> @@ -62,6 +56,6 @@ export const DesktopNav: FC<{ navItems: NavItem[] }> = ({ navItems }) => { </Popover> </Flex> ))} - </Stack> + </Flex> ); }; diff --git a/src/app/_components/NavBar/DesktopSubNav.tsx b/src/app/_components/NavBar/DesktopSubNav.tsx index 95869e206..a8b288c50 100644 --- a/src/app/_components/NavBar/DesktopSubNav.tsx +++ b/src/app/_components/NavBar/DesktopSubNav.tsx @@ -1,33 +1,40 @@ +import { LightMode } from '@chakra-ui/react'; import React from 'react'; import { Box } from '../../../ui/Box'; import { Flex } from '../../../ui/Flex'; -import { Stack } from '../../../ui/Stack'; +import { Link } from '../../../ui/Link'; import { NavItem } from './types'; -export const DesktopSubNav = ({ - label, - href, - onClick, - hasDivider, -}: NavItem & { hasDivider: boolean }) => { - const as = onClick ? 'button' : href ? 'a' : 'div'; +export const DesktopSubNav = ({ label, href, onClick }: NavItem) => { return ( - <Flex alignItems={'center'} borderBottomWidth={hasDivider ? '1px' : '0px'}> - <Box - as={as} - {...(as === 'button' ? { onClick } : { href })} - role={'group'} - display={'block'} - rounded={'md'} - color={`midnight`} - width={'100%'} - padding={'0 20px'} + <LightMode> + <Flex + alignItems={'center'} + borderBottom="1px" + _last={{ + borderBottom: 'none', + }} > - <Stack direction={'row'} align={'center'}> - <Box width={'100%'}>{label}</Box> - </Stack> - </Box> - </Flex> + {href ? ( + <Link href={href} display="block" rounded="md" color="black" width="full" px={5}> + {label} + </Link> + ) : ( + <Box + as={'button'} + onClick={onClick} + role="group" + display="block" + rounded="md" + color="black" + width="full" + px={5} + > + {label} + </Box> + )} + </Flex> + </LightMode> ); }; diff --git a/src/app/_components/NavBar/Logo.tsx b/src/app/_components/NavBar/Logo.tsx index 45d0c8116..b3538b40b 100644 --- a/src/app/_components/NavBar/Logo.tsx +++ b/src/app/_components/NavBar/Logo.tsx @@ -1,22 +1,20 @@ import { FC } from 'react'; import { ExplorerLink } from '../../../common/components/ExplorerLinks'; -import { IconButton } from '../../../ui/IconButton'; +import { Icon } from '../../../ui/Icon'; import { StxIcon } from '../../../ui/icons'; export const Logo: FC = () => { return ( <ExplorerLink href={'/'}> - <a> - <IconButton - size="42px" - icon={<StxIcon strokeWidth={1.5} size="24px" color="white" />} - flexShrink={0} - aria-label="Homepage" - title="Stacks Explorer" - as="span" - /> - </a> + <Icon + as={StxIcon} + size="22px" + color="white" + flexShrink={0} + aria-label="Homepage" + title="Stacks Explorer" + /> </ExplorerLink> ); }; diff --git a/src/app/_components/NavBar/MobileNav.tsx b/src/app/_components/NavBar/MobileNav.tsx index c8295d771..9438ccfb9 100644 --- a/src/app/_components/NavBar/MobileNav.tsx +++ b/src/app/_components/NavBar/MobileNav.tsx @@ -1,5 +1,6 @@ import { useColorMode } from '@chakra-ui/react'; import React, { FC } from 'react'; +import { PiX } from 'react-icons/pi'; import { TbX } from 'react-icons/tb'; import { Flex } from '../../../ui/Flex'; @@ -13,35 +14,23 @@ export const MobileNav: FC<{ navItems: NavItem[]; close: () => void }> = ({ navI const colorMode = useColorMode().colorMode; return ( <Stack - display={{ md: 'none' }} position="fixed" + height="full" + width="full" + backgroundColor="bg" top={0} left={0} - height="100vh" - width="100vw" zIndex={2} - backgroundColor={`bg.${colorMode}`} - padding={'0 32px'} + padding={6} + gap={6} > - <Flex justifyContent={'space-between'} padding={'16px 0'} height={'64px'} mb={'10px'}> - <ColorModeButton - aria-label={'Change color mode'} - color={`invert.${colorMode}`} - borderWidth={'1px'} - /> - <IconButton - onClick={close} - size="40px" - color={`invert.${colorMode}`} - icon={<TbX size={'32px'} />} - aria-label={'Close menu'} - /> - </Flex> - <Flex direction={'column'} gap={'16px'}> - {navItems.map(navItem => ( - <MobileNavItem key={navItem.id} {...navItem} /> - ))} + <Flex justifyContent={'space-between'}> + <ColorModeButton aria-label={'Change color mode'} color="invert" borderWidth={'1px'} /> + <IconButton onClick={close} icon={<PiX />} aria-label={'Close menu'} /> </Flex> + {navItems.map(navItem => ( + <MobileNavItem key={navItem.id} {...navItem} /> + ))} </Stack> ); }; diff --git a/src/app/_components/NavBar/MobileNavItem.tsx b/src/app/_components/NavBar/MobileNavItem.tsx index 0515d989d..f7d8d8e4f 100644 --- a/src/app/_components/NavBar/MobileNavItem.tsx +++ b/src/app/_components/NavBar/MobileNavItem.tsx @@ -1,5 +1,5 @@ import { FC } from 'react'; -import { BsChevronDown } from 'react-icons/bs'; +import { PiCaretDown } from 'react-icons/pi'; import { Box } from '../../../ui/Box'; import { Collapse } from '../../../ui/Collapse'; @@ -14,7 +14,7 @@ export const MobileNavItem: FC<NavItem> = ({ label, onClick, children, href }) = const { isOpen, onToggle } = useDisclosure(); return ( - <Stack spacing={4} onClick={children && onToggle}> + <Stack gap={4} onClick={children && onToggle} p={1}> <Flex as="a" href={href ?? '#'} @@ -23,11 +23,12 @@ export const MobileNavItem: FC<NavItem> = ({ label, onClick, children, href }) = _hover={{ textDecoration: 'none', }} + pr={2} > - <Text fontSize={'24px'}>{label}</Text> + <Text>{label}</Text> {children && ( <Icon - as={BsChevronDown} + as={PiCaretDown} transition={'all .25s ease-in-out'} transform={isOpen ? 'rotate(180deg)' : ''} w={4} @@ -35,30 +36,21 @@ export const MobileNavItem: FC<NavItem> = ({ label, onClick, children, href }) = /> )} </Flex> - - <Collapse in={isOpen} animateOpacity style={{ marginTop: '0 !important' }}> - <Stack - mt={2} - pl={4} - borderLeft={1} - borderStyle={'solid'} - borderColor={'pink'} - align={'start'} - > - {children && - children.map(child => { - const as = child.onClick ? 'button' : child.href ? 'a' : 'div'; - return ( - <Box - as={as} - key={child.id} - width={'100%'} - {...(as === 'button' ? { onClick: child.onClick } : { href: child.href })} - > - {child.label} - </Box> - ); - })} + <Collapse in={isOpen}> + <Stack mt={2} pl={4} borderLeft={'2px solid var(--stacks-colors-purple-400)'}> + {children?.map(child => { + const as = child.onClick ? 'button' : child.href ? 'a' : 'div'; + return ( + <Box + as={as} + key={child.id} + color="invert" + {...(as === 'button' ? { onClick: child.onClick } : { href: child.href })} + > + {child.label} + </Box> + ); + })} </Stack> </Collapse> </Stack> diff --git a/src/app/_components/NavBar/NavLabel.tsx b/src/app/_components/NavBar/NavLabel.tsx index f28220477..c19a6815b 100644 --- a/src/app/_components/NavBar/NavLabel.tsx +++ b/src/app/_components/NavBar/NavLabel.tsx @@ -1,18 +1,16 @@ -import { useColorMode } from '@chakra-ui/react'; import React, { FC, ReactNode } from 'react'; -import { BsChevronRight } from 'react-icons/bs'; +import { PiCaretRight } from 'react-icons/pi'; import { Flex } from '../../../ui/Flex'; import { Icon } from '../../../ui/Icon'; import { Text } from '../../../ui/Text'; export const NavLabel: FC<{ children: ReactNode }> = ({ children }) => ( - <Flex height={'50px'} alignItems={'center'}> + <Flex height={'50px'} alignItems={'center'} color="black"> <Text - color={`textTitle.${useColorMode().colorMode}`} _groupHover={{ textDecoration: 'underline' }} - fontWeight={500} - fontSize={'13px'} + fontWeight={'medium'} + fontSize={'xs'} textAlign={'left'} > {children} @@ -26,7 +24,7 @@ export const NavLabel: FC<{ children: ReactNode }> = ({ children }) => ( align={'center'} flex={1} > - <Icon w={3} h={3} as={BsChevronRight} color={`textCaption.${useColorMode().colorMode}`} /> + <Icon w={3} h={3} as={PiCaretRight} /> </Flex> </Flex> ); diff --git a/src/app/_components/NavBar/NetworkLabel.tsx b/src/app/_components/NavBar/NetworkLabel.tsx index b8e7d4ce7..c01c8bf94 100644 --- a/src/app/_components/NavBar/NetworkLabel.tsx +++ b/src/app/_components/NavBar/NetworkLabel.tsx @@ -1,5 +1,6 @@ import { useColorMode } from '@chakra-ui/react'; import React, { FC } from 'react'; +import { PiTrash } from 'react-icons/pi'; import { TbCheck, TbTrash } from 'react-icons/tb'; import { ChainID } from '@stacks/transactions'; @@ -13,6 +14,7 @@ import { Network } from '../../../common/types/network'; import { buildUrl } from '../../../common/utils/buildUrl'; import { Box } from '../../../ui/Box'; import { Flex } from '../../../ui/Flex'; +import { Icon } from '../../../ui/Icon'; import { IconButton } from '../../../ui/IconButton'; import { Spinner } from '../../../ui/Spinner'; import { Stack } from '../../../ui/Stack'; @@ -55,6 +57,7 @@ export const NetworkLabel: FC<{ network: Network }> = ({ network }) => { padding={'15px 0'} opacity={isDisabled ? 0.5 : 1} cursor={isDisabled ? 'not-allowed' : 'unset'} + gap={2} > <Stack as={isDisabled || isActive ? 'div' : 'a'} @@ -92,11 +95,7 @@ export const NetworkLabel: FC<{ network: Network }> = ({ network }) => { position="relative" color={`textCaption.${colorMode}`} size={'21px'} - icon={ - <span> - <TbTrash size={'21px'} /> - </span> - } + icon={<Icon as={PiTrash} size={4} />} onClick={() => removeCustomNetwork(network)} aria-label={'Remove network'} _hover={{ bg: 'rgba(255, 255, 255, 0.25)' }} diff --git a/src/app/_components/NavBar/NetworkModeBanner.tsx b/src/app/_components/NavBar/NetworkModeBanner.tsx index 418495c17..897a6071e 100644 --- a/src/app/_components/NavBar/NetworkModeBanner.tsx +++ b/src/app/_components/NavBar/NetworkModeBanner.tsx @@ -2,6 +2,7 @@ import { useColorMode } from '@chakra-ui/react'; import { FC } from 'react'; +import { PiFlask } from 'react-icons/pi'; import { Badge } from '../../../common/components/Badge'; import { useGlobalContext } from '../../../common/context/useAppContext'; @@ -9,7 +10,6 @@ import { capitalize } from '../../../common/utils/utils'; import { Flex } from '../../../ui/Flex'; import { Icon } from '../../../ui/Icon'; import { Text } from '../../../ui/Text'; -import { FlaskIcon } from '../../../ui/icons'; export const NetworkModeBanner: FC = () => { const networkMode = useGlobalContext().activeNetwork.mode; @@ -18,9 +18,9 @@ export const NetworkModeBanner: FC = () => { return ( <Badge bg="white" border={'none'}> <Flex alignItems="center" as={'span'}> - <Icon as={FlaskIcon} color={`brand.${colorMode}`} size="16px" mr="4px" /> - <Text color={`textTitle.light`} whiteSpace={'nowrap'} as={'span'}> - {capitalize(networkMode)} mode + <Icon as={PiFlask} color={'black'} size={4} /> + <Text color={'black'} whiteSpace={'nowrap'} as={'span'}> + {capitalize(networkMode)} </Text> </Flex> </Badge> diff --git a/src/app/_components/NavBar/__tests__/__snapshots__/DesktopNav.test.tsx.snap b/src/app/_components/NavBar/__tests__/__snapshots__/DesktopNav.test.tsx.snap index 4edf505e7..89d7852ad 100644 --- a/src/app/_components/NavBar/__tests__/__snapshots__/DesktopNav.test.tsx.snap +++ b/src/app/_components/NavBar/__tests__/__snapshots__/DesktopNav.test.tsx.snap @@ -3,38 +3,37 @@ exports[`DesktopNav should render correctly 1`] = ` <DocumentFragment> <div - class="chakra-stack css-1yubk4m" + class="css-ffmerx" > <div - class="css-1txr7ua" + class="css-70qvj9" > <div aria-controls="popover-content-:r1:" aria-expanded="false" aria-haspopup="dialog" - class="css-1dz1cl" + class="css-j93dqc" id="popover-trigger-:r1:" > <a - class="css-qrie7s" + class="chakra-link css-uzv5li" href="#" > Sample Item 1 </a> <svg - class="chakra-icon css-ys7se3" + class="chakra-icon css-g16cky" fill="currentColor" focusable="false" - height="3" + height="3.5" stroke="currentColor" stroke-width="0" - viewBox="0 0 16 16" - width="3" + viewBox="0 0 256 256" + width="3.5" xmlns="http://www.w3.org/2000/svg" > <path - d="M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z" - fill-rule="evenodd" + d="M213.66,101.66l-80,80a8,8,0,0,1-11.32,0l-80-80A8,8,0,0,1,53.66,90.34L128,164.69l74.34-74.35a8,8,0,0,1,11.32,11.32Z" /> </svg> </div> @@ -43,7 +42,7 @@ exports[`DesktopNav should render correctly 1`] = ` style="visibility: hidden; position: absolute; min-width: max-content; inset: 0 auto auto 0;" > <section - class="chakra-popover__content css-ztk6kg" + class="chakra-popover__content css-1kjb8d7" id="popover-content-:r1:" role="tooltip" style="transform-origin: var(--popper-transform-origin); opacity: 0; visibility: hidden; transform: scale(0.95) translateZ(0);" @@ -52,17 +51,17 @@ exports[`DesktopNav should render correctly 1`] = ` </div> </div> <div - class="css-1txr7ua" + class="css-70qvj9" > <div aria-controls="popover-content-:r3:" aria-expanded="false" aria-haspopup="dialog" - class="css-1dz1cl" + class="css-j93dqc" id="popover-trigger-:r3:" > <a - class="css-kk07nh" + class="chakra-link css-yne82k" href="#" > Sample Item 2 diff --git a/src/app/_components/NavBar/__tests__/__snapshots__/MobileNav.test.tsx.snap b/src/app/_components/NavBar/__tests__/__snapshots__/MobileNav.test.tsx.snap index 5ccb52c89..3693563ef 100644 --- a/src/app/_components/NavBar/__tests__/__snapshots__/MobileNav.test.tsx.snap +++ b/src/app/_components/NavBar/__tests__/__snapshots__/MobileNav.test.tsx.snap @@ -3,147 +3,119 @@ exports[`MobileNav should render correctly 1`] = ` <DocumentFragment> <div - class="chakra-stack css-4xpai1" + class="chakra-stack css-1kyhbj8" > <div - class="css-sw0q0z" + class="css-gg4vpm" > <button aria-label="Change color mode" - class="chakra-button css-wulgol" + class="chakra-button css-1fthbo8" title="Toggle color mode" type="button" > <svg aria-hidden="true" - fill="none" + fill="currentColor" focusable="false" height="1em" stroke="currentColor" - stroke-linecap="round" - stroke-linejoin="round" - stroke-width="2" - viewBox="0 0 24 24" + stroke-width="0" + viewBox="0 0 256 256" width="1em" xmlns="http://www.w3.org/2000/svg" > <path - d="M0 0h24v24H0z" - fill="none" - stroke="none" - /> - <path - d="M3 3l18 18" - /> - <path - d="M16 12a4 4 0 0 0 -4 -4m-2.834 1.177a4 4 0 0 0 5.66 5.654" - /> - <path - d="M3 12h1m8 -9v1m8 8h1m-9 8v1m-6.4 -15.4l.7 .7m12.1 -.7l-.7 .7m0 11.4l.7 .7m-12.1 -.7l-.7 .7" + d="M233.54,142.23a8,8,0,0,0-8-2,88.08,88.08,0,0,1-109.8-109.8,8,8,0,0,0-10-10,104.84,104.84,0,0,0-52.91,37A104,104,0,0,0,136,224a103.09,103.09,0,0,0,62.52-20.88,104.84,104.84,0,0,0,37-52.91A8,8,0,0,0,233.54,142.23ZM188.9,190.34A88,88,0,0,1,65.66,67.11a89,89,0,0,1,31.4-26A106,106,0,0,0,96,56,104.11,104.11,0,0,0,200,160a106,106,0,0,0,14.92-1.06A89,89,0,0,1,188.9,190.34Z" /> </svg> </button> <button aria-label="Close menu" - class="chakra-button css-11sr1fv" + class="chakra-button css-12w4gwb" type="button" > <svg aria-hidden="true" - fill="none" + fill="currentColor" focusable="false" - height="32px" + height="1em" stroke="currentColor" - stroke-linecap="round" - stroke-linejoin="round" - stroke-width="2" - viewBox="0 0 24 24" - width="32px" + stroke-width="0" + viewBox="0 0 256 256" + width="1em" xmlns="http://www.w3.org/2000/svg" > <path - d="M0 0h24v24H0z" - fill="none" - stroke="none" - /> - <path - d="M18 6l-12 12" - /> - <path - d="M6 6l12 12" + d="M205.66,194.34a8,8,0,0,1-11.32,11.32L128,139.31,61.66,205.66a8,8,0,0,1-11.32-11.32L116.69,128,50.34,61.66A8,8,0,0,1,61.66,50.34L128,116.69l66.34-66.35a8,8,0,0,1,11.32,11.32L139.31,128Z" /> </svg> </button> </div> <div - class="css-xar605" + class="chakra-stack css-1q7sb2n" > - <div - class="chakra-stack css-bi0nzx" + <a + class="css-tz7dko" + href="#" > - <a - class="css-3xyie7" - href="#" + <span + class="chakra-text css-13o7eu2" > - <span - class="chakra-text css-pjr8e0" - > - Mobile Sample Item 1 - </span> - <svg - class="chakra-icon css-3tv48m" - fill="currentColor" - focusable="false" - height="1em" - stroke="currentColor" - stroke-width="0" - viewBox="0 0 16 16" - width="1em" - xmlns="http://www.w3.org/2000/svg" - > - <path - d="M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z" - fill-rule="evenodd" - /> - </svg> - </a> + Mobile Sample Item 1 + </span> + <svg + class="chakra-icon css-3tv48m" + fill="currentColor" + focusable="false" + height="1em" + stroke="currentColor" + stroke-width="0" + viewBox="0 0 256 256" + width="1em" + xmlns="http://www.w3.org/2000/svg" + > + <path + d="M213.66,101.66l-80,80a8,8,0,0,1-11.32,0l-80-80A8,8,0,0,1,53.66,90.34L128,164.69l74.34-74.35a8,8,0,0,1,11.32,11.32Z" + /> + </svg> + </a> + <div + class="chakra-collapse" + style="overflow: hidden; display: none; opacity: 0; height: 0px;" + > <div - class="chakra-collapse" - style="overflow: hidden; display: none; opacity: 0; height: 0px;" + class="chakra-stack css-1s2q0he" > - <div - class="chakra-stack css-k96o6u" + <a + class="css-1ak55br" + href="#" > - <a - class="css-9sazr8" - href="#" - > - Mobile Sub Item 1 - </a> - </div> + Mobile Sub Item 1 + </a> </div> </div> - <div - class="chakra-stack css-bi0nzx" + </div> + <div + class="chakra-stack css-1q7sb2n" + > + <a + class="css-tz7dko" + href="#" > - <a - class="css-3xyie7" - href="#" + <span + class="chakra-text css-13o7eu2" > - <span - class="chakra-text css-pjr8e0" - > - Mobile Sample Item 2 - </span> - </a> + Mobile Sample Item 2 + </span> + </a> + <div + class="chakra-collapse" + style="overflow: hidden; display: none; opacity: 0; height: 0px;" + > <div - class="chakra-collapse" - style="overflow: hidden; display: none; opacity: 0; height: 0px;" - > - <div - class="chakra-stack css-k96o6u" - /> - </div> + class="chakra-stack css-1s2q0he" + /> </div> </div> </div> diff --git a/src/app/_components/NavBar/index.tsx b/src/app/_components/NavBar/index.tsx index 50557e137..7211a283b 100644 --- a/src/app/_components/NavBar/index.tsx +++ b/src/app/_components/NavBar/index.tsx @@ -1,19 +1,21 @@ 'use client'; import { FC, useMemo } from 'react'; -import { RxHamburgerMenu } from 'react-icons/rx'; +import { PiList } from 'react-icons/pi'; import { openModal } from '../../../common/components/modals/modal-slice'; -import { MODALS, PAGE_MAX_WIDTH } from '../../../common/constants/constants'; +import { MODALS } from '../../../common/constants/constants'; import { useGlobalContext } from '../../../common/context/useAppContext'; import { useAppDispatch } from '../../../common/state/hooks'; import { Network } from '../../../common/types/network'; import { buildUrl } from '../../../common/utils/buildUrl'; +import { capitalize } from '../../../common/utils/utils'; import { Search } from '../../../features/search/Search'; import { Box } from '../../../ui/Box'; import { Flex } from '../../../ui/Flex'; import { Icon } from '../../../ui/Icon'; import { IconButton } from '../../../ui/IconButton'; +import { Show } from '../../../ui/Show'; import { useDisclosure } from '../../../ui/hooks/useDisclosure'; import { BtcStxPrice } from './BtcStxPrice'; import { ColorModeButton } from './ColorModeButton'; @@ -27,11 +29,7 @@ import { NavItem } from './types'; export const NavBar: FC = () => { const { isOpen, onToggle } = useDisclosure(); - const { - networks, - activeNetwork, - apiUrls: { mainnet, testnet }, - } = useGlobalContext(); + const { networks, activeNetwork } = useGlobalContext(); const dispatch = useAppDispatch(); const navItems: NavItem[] = useMemo( @@ -64,7 +62,7 @@ export const NavBar: FC = () => { }, { id: 'network', - label: 'Network', + label: capitalize(activeNetwork.mode), children: [ ...Object.values<Network>(networks).map((network, key) => { return { @@ -86,45 +84,30 @@ export const NavBar: FC = () => { ); return ( - <Box - width="100%" - maxWidth={PAGE_MAX_WIDTH} - margin={'auto'} - padding={{ base: '0 16px', md: '0 32px' }} - > - <Flex align={'center'} justifyContent={'space-between'}> - <Flex - flex={{ base: 1 }} - alignItems={'center'} - height={'64px'} - gap={'16px'} - justifyContent={'space-between'} - > - <Logo /> - <Search - display={['none', 'none', 'block', 'block']} - variant="small" - width="100%" - maxWidth="760px" - /> + <Box width="full"> + <Flex alignItems={'center'} flex={{ base: 1 }} gap={6}> + <Logo /> + <Search /> + <Show above="lg"> <NetworkModeBanner /> - <Flex display={{ base: 'none', md: 'flex' }} gap={'16px'}> + </Show> + <Show above="lg"> + <Flex gap={3}> <ColorModeButton aria-label={'Change color mode'} /> <DesktopNav navItems={navItems} /> - <BtcStxPrice /> </Flex> - </Flex> - - <Flex display={{ base: 'flex', md: 'none' }}> + <BtcStxPrice /> + </Show> + <Show below="lg"> <IconButton onClick={onToggle} - icon={<Icon as={RxHamburgerMenu} w={5} h={5} color={'#fff'} />} + icon={<Icon as={PiList} w={6} h={6} color={'white'} />} variant={'ghost'} aria-label={'Toggle Navigation'} /> - </Flex> + {isOpen && <MobileNav navItems={navItems} close={onToggle} />} + </Show> </Flex> - {isOpen && <MobileNav navItems={navItems} close={onToggle} />} </Box> ); }; diff --git a/src/app/_components/Notice.tsx b/src/app/_components/Notice.tsx deleted file mode 100644 index cfc51135f..000000000 --- a/src/app/_components/Notice.tsx +++ /dev/null @@ -1,58 +0,0 @@ -'use client'; - -import React from 'react'; - -import { PAGE_MAX_WIDTH } from '../../common/constants/constants'; -import { Box } from '../../ui/Box'; -import { ExclamationMarkCircleIcon } from '../../ui/ExclamationMarkCircleIcon'; -import { Flex, FlexProps } from '../../ui/Flex'; -import { Text } from '../../ui/Text'; - -export const Notice = ({ - label, - message, - ...rest -}: { label?: string; message?: string } & FlexProps) => ( - <Box - mx="auto" - borderRadius="8px" - maxWidth="1216px" - bg="var(--colors-bg-alt)" - borderBottomWidth="1px" - borderColor="var(--stacks-colors-border)" - {...rest} - > - <Flex - p="8px" - alignItems={['flex-start', 'flex-start', 'center']} - justifyContent={['center', 'center', 'flex-start']} - mx="auto" - width="100%" - maxWidth={PAGE_MAX_WIDTH} - flexDirection={['column', 'column', 'row']} - px={['16px']} - > - <Flex alignItems="center" mb={['8px', '8px', 'unset']}> - <Box mr="8px" color="orange"> - <ExclamationMarkCircleIcon size="14px" /> - </Box> - {label ? ( - <Text - mr="8px" - fontWeight="600" - fontSize="14px" - lineHeight="22px" - color="var(--colors-text-title)" - > - {label} - </Text> - ) : null} - </Flex> - {message ? ( - <Text fontSize="14px" lineHeight="22px" color="var(--colors-text-body)"> - {message} - </Text> - ) : null} - </Flex> - </Box> -); diff --git a/src/app/_components/PageTitle.tsx b/src/app/_components/PageTitle.tsx index 49d8d24c3..951cc2dfb 100644 --- a/src/app/_components/PageTitle.tsx +++ b/src/app/_components/PageTitle.tsx @@ -1,23 +1,37 @@ import { ReactNode } from 'react'; +import * as React from 'react'; -import { TextProps, Title } from '../../ui/typography'; +import { TxTypeTag } from '../../common/components/TxTypeTag'; +import { TxStatusLabel } from '../../common/components/status'; +import { getTransactionStatus } from '../../common/utils/transactions'; +import { getTxTitle } from '../../common/utils/utils'; +import { Badge } from '../../ui/Badge'; +import { Flex } from '../../ui/Flex'; +import { HStack } from '../../ui/HStack'; +import { Heading, HeadingProps } from '../../ui/Heading'; +import { TxAlerts } from '../txid/[txId]/TxAlerts'; -export function PageTitle({ children, ...props }: { children: ReactNode } & TextProps) { +export function PageTitle({ children, ...props }: { children: ReactNode } & HeadingProps) { return ( - <Title + <Heading as="h1" - fontSize="36px" - display="block" - width="100%" - mt="40px" + fontWeight={'medium'} + fontSize="4xl" + mt={20} mb="0" - data-test="homepage-title" - color="white" - gridColumnStart={'1'} - gridColumnEnd={['2', '2', '3']} + color={'slate.50'} {...props} > {children} - + + ); +} + +export function PageTitleWithTags({ children, tags }: { children: ReactNode; tags?: ReactNode }) { + return ( + + {tags} + {children} + ); } diff --git a/src/app/_components/PageWrapper.tsx b/src/app/_components/PageWrapper.tsx index 790b07bb9..ff50d9c51 100644 --- a/src/app/_components/PageWrapper.tsx +++ b/src/app/_components/PageWrapper.tsx @@ -1,43 +1,26 @@ 'use client'; -import { useColorMode } from '@chakra-ui/react'; -import React, { FC, ReactNode } from 'react'; +import { useColorModeValue } from '@chakra-ui/react'; +import React, { ReactNode } from 'react'; import { AddNetworkModal } from '../../common/components/modals/AddNetwork'; -import { PAGE_MAX_WIDTH } from '../../common/constants/constants'; -import { - SITE_NOTICE_BANNER_LABEL, - SITE_NOTICE_BANNER_MESSAGE, - SITE_NOTICE_ENABLED, -} from '../../common/constants/env'; -import { Search } from '../../features/search/Search'; -import { Box } from '../../ui/Box'; import { Flex } from '../../ui/Flex'; import { Footer } from './Footer'; import { NavBar } from './NavBar'; import { NetworkModeToast } from './NetworkModeToast'; -import { Notice } from './Notice'; -import { StatusBar } from './StatusBar'; - -export const PageWrapper: FC<{ children: ReactNode }> = ({ children }) => { - const colorMode = useColorMode().colorMode; +import { IncidentsStatusBarWithErrorBoundary } from './StatusBar'; +function WrapperWithBg({ children }: { children: ReactNode }) { return ( - - - - - = ({ children }) => { #0F102B 93.08% ), white`, - backgroundRepeat: 'no-repeat, repeat', - backgroundSize: '100% 530px, 100% 100%', - } - : undefined - } - > - + `linear-gradient( + 29.53deg, + #9528F7 2.94%, + #522DE7 39.91%, + #221A71 76.87%, + #0F102B 93.08% + ), + black` + )} + bgRepeat="no-repeat, repeat" + bgSize="100% 530px, 100% 100%" + > + {children} + + ); +} + +export function PageWrapper({ children }: { children: ReactNode }) { + return ( + <> + + + - - - - - {SITE_NOTICE_ENABLED && ( - - - - )} - - {children} - -