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);
+}