diff --git a/apps/web/src/App.tsx b/apps/web/src/App.tsx index 0d4d3dbb0..bcdabf100 100644 --- a/apps/web/src/App.tsx +++ b/apps/web/src/App.tsx @@ -11,7 +11,8 @@ import { GlobalListStyle } from '@tonkeeper/uikit/dist/components/List'; import { Loading } from '@tonkeeper/uikit/dist/components/Loading'; import MemoryScroll from '@tonkeeper/uikit/dist/components/MemoryScroll'; import { - ActivitySkeletonPage, BrowserSkeletonPage, + ActivitySkeletonPage, + BrowserSkeletonPage, CoinSkeletonPage, HomeSkeleton, SettingsSkeletonPage diff --git a/packages/uikit/src/components/Header.tsx b/packages/uikit/src/components/Header.tsx index e34e2a0d5..920691f79 100644 --- a/packages/uikit/src/components/Header.tsx +++ b/packages/uikit/src/components/Header.tsx @@ -15,7 +15,7 @@ import { H1, H3, Label1, Label2 } from './Text'; import { ScanButton } from './connect/ScanButton'; import { ImportNotification } from './create/ImportNotification'; import { useUserCountry } from '../state/country'; -import { SkeletonText } from './Skeleton'; +import { SkeletonText } from './shared/Skeleton'; const Block = styled.div<{ center?: boolean; diff --git a/packages/uikit/src/components/Skeleton.tsx b/packages/uikit/src/components/Skeleton.tsx index 00c8a29b9..062efc1bb 100644 --- a/packages/uikit/src/components/Skeleton.tsx +++ b/packages/uikit/src/components/Skeleton.tsx @@ -1,8 +1,8 @@ import React, { FC, useEffect } from 'react'; -import styled, { css } from 'styled-components'; +import styled from 'styled-components'; import { useAppSdk } from '../hooks/appSdk'; import { InnerBody } from './Body'; -import { ActivityHeader, BrowserHeader, SettingsHeader } from './Header'; +import {ActivityHeader, BrowserHeader, SettingsHeader} from './Header'; import { ActionsRow } from './home/Actions'; import { BalanceSkeleton } from './home/Balance'; import { CoinInfoSkeleton } from './jettons/Info'; @@ -10,104 +10,9 @@ import { ColumnText } from './Layout'; import { ListBlock, ListItem, ListItemPayload } from './List'; import { SubHeader } from './SubHeader'; import { H3 } from './Text'; -import { RecommendationsPageBodySkeleton } from './skeletons/BrowserSkeletons'; - -function randomIntFromInterval(min: number, max: number) { - return Math.floor(Math.random() * (max - min + 1) + min); -} - -function randomWidth() { - return randomIntFromInterval(30, 90) + '%'; -} - -const Base = styled.div` - display: inline-block; - - @keyframes placeHolderShimmer { - 0% { - background-position: -800px 0; - } - 100% { - background-position: 800px 0; - } - } - - animation-duration: 2s; - animation-fill-mode: forwards; - animation-iteration-count: infinite; - animation-name: placeHolderShimmer; - animation-timing-function: linear; - background-color: #f6f7f8; - background: linear-gradient(to right, #4f5a70 8%, #bbbbbb 18%, #4f5a70 33%); - background-size: 800px 104px; - - opacity: 0.1; - - position: relative; -`; -const Block = styled(Base)<{ size?: string; width?: string }>` - border-radius: ${props => props.theme.cornerExtraExtraSmall}; - - ${props => css` - width: ${props.width ?? randomWidth()}; - `} - - ${props => { - switch (props.size) { - case 'large': - return css` - height: 1.5rem; - `; - case 'small': - return css` - height: 0.5rem; - `; - default: - return css` - height: 1rem; - `; - } - }} -`; - -export const SkeletonText: FC<{ size?: 'large' | 'small'; width?: string; className?: string }> = - React.memo(({ size, width, className }) => { - return ; - }); - -export const Skeleton = styled(Base)<{ - width: string; - height: string; - borderRadius?: string; - margin?: string; - marginBottom?: string; -}>` - display: block; - border-radius: ${props => - props.borderRadius - ? props.theme[props.borderRadius] || props.theme.cornerExtraExtraSmall - : props.theme.cornerExtraExtraSmall}; - - ${props => css` - width: ${props.width ?? '3rem'}; - height: ${props.height ?? '20px'}; - ${props.margin && `margin: ${props.margin};`} - ${props.marginBottom && `margin-bottom: ${props.marginBottom};`} - `} -`; - -const Image = styled(Base)<{ width?: string }>` - border-radius: ${props => props.theme.cornerFull}; - - ${props => css` - width: ${props.width ?? '44px'}; - height: ${props.width ?? '44px'}; - `} -`; - -export const SkeletonImage: FC<{ width?: string }> = React.memo(({ width }) => { - return ; -}); +import { SkeletonImage, SkeletonText } from './shared/Skeleton'; +import {randomIntFromInterval} from "../libs/common"; +import {RecommendationsPageBodySkeleton} from "./skeletons/BrowserSkeletons"; export const SkeletonSubHeader = React.memo(() => { return } />; diff --git a/packages/uikit/src/components/home/Balance.tsx b/packages/uikit/src/components/home/Balance.tsx index 380e692f2..73f60ffe4 100644 --- a/packages/uikit/src/components/home/Balance.tsx +++ b/packages/uikit/src/components/home/Balance.tsx @@ -13,7 +13,7 @@ import { formatFiatCurrency } from '../../hooks/balance'; import { useTranslation } from '../../hooks/translation'; import { QueryKey } from '../../libs/queryKey'; import { TokenRate, getRateKey } from '../../state/rates'; -import { SkeletonText } from '../Skeleton'; +import { SkeletonText } from '../shared/Skeleton'; import { Body3, Label2, Num2 } from '../Text'; import { AssetData } from './Jettons'; diff --git a/packages/uikit/src/components/jettons/Info.tsx b/packages/uikit/src/components/jettons/Info.tsx index 5ed61806b..e3d313b3d 100644 --- a/packages/uikit/src/components/jettons/Info.tsx +++ b/packages/uikit/src/components/jettons/Info.tsx @@ -1,6 +1,6 @@ import React, { FC } from 'react'; import styled from 'styled-components'; -import { SkeletonImage, SkeletonText } from '../Skeleton'; +import { SkeletonImage, SkeletonText } from '../shared/Skeleton'; import { H2 } from '../Text'; import { Body } from './CroppedText'; diff --git a/packages/uikit/src/components/shared/Skeleton.tsx b/packages/uikit/src/components/shared/Skeleton.tsx new file mode 100644 index 000000000..a97e4d4a9 --- /dev/null +++ b/packages/uikit/src/components/shared/Skeleton.tsx @@ -0,0 +1,96 @@ +import styled, { css } from 'styled-components'; +import React, { FC } from 'react'; +import { randomIntFromInterval } from '../../libs/common'; + +function randomWidth() { + return randomIntFromInterval(30, 90) + '%'; +} + +const Base = styled.div` + display: inline-block; + + @keyframes placeHolderShimmer { + 0% { + background-position: -800px 0; + } + 100% { + background-position: 800px 0; + } + } + + animation-duration: 2s; + animation-fill-mode: forwards; + animation-iteration-count: infinite; + animation-name: placeHolderShimmer; + animation-timing-function: linear; + background-color: #f6f7f8; + background: linear-gradient(to right, #4f5a70 8%, #bbbbbb 18%, #4f5a70 33%); + background-size: 800px 104px; + + opacity: 0.1; + + position: relative; +`; +const Block = styled(Base)<{ size?: string; width?: string }>` + border-radius: ${props => props.theme.cornerExtraExtraSmall}; + + ${props => css` + width: ${props.width ?? randomWidth()}; + `} + + ${props => { + switch (props.size) { + case 'large': + return css` + height: 1.5rem; + `; + case 'small': + return css` + height: 0.5rem; + `; + default: + return css` + height: 1rem; + `; + } + }} +`; + +export const SkeletonText: FC<{ size?: 'large' | 'small'; width?: string; className?: string }> = + React.memo(({ size, width, className }) => { + return ; + }); + +export const Skeleton = styled(Base)<{ + width: string; + height?: string; + borderRadius?: string; + margin?: string; + marginBottom?: string; +}>` + display: block; + border-radius: ${props => + props.borderRadius + ? props.theme[props.borderRadius] || props.theme.cornerExtraExtraSmall + : props.theme.cornerExtraExtraSmall}; + + ${props => css` + width: ${props.width ?? '3rem'}; + height: ${props.height ?? '20px'}; + ${props.margin && `margin: ${props.margin};`} + ${props.marginBottom && `margin-bottom: ${props.marginBottom};`} + `} +`; + +const Image = styled(Base)<{ width?: string }>` + border-radius: ${props => props.theme.cornerFull}; + + ${props => css` + width: ${props.width ?? '44px'}; + height: ${props.width ?? '44px'}; + `} +`; + +export const SkeletonImage: FC<{ width?: string }> = React.memo(({ width }) => { + return ; +}); diff --git a/packages/uikit/src/components/skeletons/BrowserSkeletons.tsx b/packages/uikit/src/components/skeletons/BrowserSkeletons.tsx index 9338846b0..d41cab06c 100644 --- a/packages/uikit/src/components/skeletons/BrowserSkeletons.tsx +++ b/packages/uikit/src/components/skeletons/BrowserSkeletons.tsx @@ -1,5 +1,5 @@ import { PromotedItem, PromotedItemText } from '../../pages/browser/PromotedItem'; -import { Skeleton } from '../Skeleton'; +import { Skeleton } from '../shared/Skeleton'; import React, { FC } from 'react'; import { ListBlock, ListItem } from '../List'; import styled from 'styled-components'; @@ -16,15 +16,15 @@ const Heading = styled.div` gap: 1rem; `; +const CarouselSkeleton = styled(Skeleton)` + height: auto; + aspect-ratio: 2 / 1; +`; + export const RecommendationsPageBodySkeleton: FC = () => { return ( <> - + diff --git a/packages/uikit/src/libs/common.ts b/packages/uikit/src/libs/common.ts index e3340af96..cae49b453 100644 --- a/packages/uikit/src/libs/common.ts +++ b/packages/uikit/src/libs/common.ts @@ -34,3 +34,7 @@ export function mergeRefs(...inputRefs: (Ref | undefined)[]): Ref | Ref }); }; } + +export function randomIntFromInterval(min: number, max: number) { + return Math.floor(Math.random() * (max - min + 1) + min); +}