Skip to content

Commit

Permalink
[GSW-2033] swap renewal (#598)
Browse files Browse the repository at this point in the history
* feat: [GSW-2033] Swap Renewal (TokenPriceChart LineGraph)

* feat: [GSW-2033] Swap Renewal (TokenPriceChart LineGraph)

* feat: [GSW-2033] Swap Renewal (TokenPriceChart LineGraph)

* fix: [GSW-2033] Token Price Chart

* feat: [GSW-2033] display default token when no tokens are selected

* feat: [GSW-2033] add mouseOut handler for chart reset

* feat: [GSW-2033] i18n

* fix: [GSW-2033] Change chart time notation

* feat: [GSW-2033] Improve Swap Chart Gradient position & loading

* feat: [GSW-2033] Remove the top and bottom coordinate (price range) lines

* fix: [GSW-2033] Improve mobile UI

* feat: [GSW-2033] TokenPair Transaction list UI

* fix: Not fetching when typing after the maximum number of decimal places

* fix: [GSW-2033] skip loading state for messages ending with period

* fix: [GSW-2033] Improve UI

* fix: [GSW-2033] Improve UI

* fix: [GSW-2033] Response layout

* fix: [GSW-2033] Improve layout

* fix: [GSW-2033] Improve UI

* fix: [GSW-2033] Improve UI

* fix: [GSW-2033] Improve UI

* feat: [GSW-2033] Amount Input UI renewal

* feat: [GSW-2033] Amount Input UI renewal in TokenDetail

* feat: [GSW-2033] Amount Input UI renewal in EarnAddPosition

* feat: [GSW-2033] Amount Input UI renewal in Wallet Send

* feat: [GSW-2033] Amount Input UI renewal in Launchpad

* fix: [GSW-2033] path-link-icon color

* refactor: [GSW-2033] Extract this nested ternary

* fix: [GSW-2033] Improve AmountInput UI

* Revert "refactor: [GSW-2033] Extract this nested ternary"

This reverts commit 632104e.

* fix: [GSW-2033] UI

* refactor: [GSW-2033] SonarQube issue

* fix: [GSW-2033] Improved when loading is not visible

* fix: [GSW-2033] TokenSwapChart Mobile Hover UI

* fix: [GSW-2033] Improve UI

* feat: [GSW-2033] refetching UI

* feat: [GSW-2033] Improve User Typing state

* fix: [GSW-2033] MaxButton size

* fix: [GSW-2033] TokenPath Responsive UI

* fix: [GSW-2033] TokenSelect Modal UI Issue

* fix: [GSW-2033] TokenPath UI

* feat: [GSW-2033] Improve UI

* fix: [GSW-2033] Mobile Chart Hover

* fix: [GSW-2033] Native TokenDetail Link

* fix: [GSW-2033] Native TokenDetail Link

* fix: [GSW-2033] TokenPath LinkIcon UI

* refactor: [GSW-2033] SonarQube issue

* refactor: [GSW-2033] SonarQube issue

* fix: [GSW-2033] useElementWidthList

* fix: [GSW-2033] useElementWidthList

* refactor: [GSW-2033] refactor: Improve readability of graph hover logic with early return

* refactor: [GSW-2033] apply PR review feedback
  • Loading branch information
tfrg authored Jan 7, 2025
1 parent ea9ea82 commit 5039e83
Show file tree
Hide file tree
Showing 50 changed files with 1,437 additions and 296 deletions.
1 change: 1 addition & 0 deletions packages/web/src/components/common/divider/divider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ import styled from "@emotion/styled";
export const Divider = styled.div`
width: 100%;
border-top: 1px solid ${({ theme }) => theme.color.border02};
margin: 6px 0;
`;
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ export const ModalContainer = styled.div`
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
direction: rtl;
}
padding: 1.5px 4px;
${mixins.flexbox("row", "center", "flex-start")};
Expand Down Expand Up @@ -267,28 +268,39 @@ export const Overlay = styled.div`
`;

interface Props {
containerWidth: number;
maxWidth: number;
tokenNameWidthList: number;
}

export const TokenInfoWrapper = styled.div<Props>`
overflow-x: hidden;
max-width: ${({ maxWidth }) => {
return `calc(460px - 150px - ${maxWidth}px)`;
max-width: ${({ containerWidth, maxWidth }) => {
return `calc(${containerWidth}px - 150px - ${maxWidth}px)`;
}};
width: 100%;
.token-path {
max-width: ${({ tokenNameWidthList, maxWidth }) => {
return `calc(460px - 158px - ${maxWidth}px - ${tokenNameWidthList}px)`;
max-width: ${({ containerWidth, tokenNameWidthList, maxWidth }) => {
return `calc(${containerWidth}px - 158px - ${maxWidth}px - ${tokenNameWidthList}px)`;
}};
}
${media.mobile} {
max-width: ${({ maxWidth }) => {
return `calc(328px - 96px - ${maxWidth}px)`;
max-width: ${({ containerWidth, maxWidth }) => {
return `calc(${containerWidth}px - 96px - ${maxWidth}px)`;
}};
.token-path {
max-width: ${({ tokenNameWidthList, maxWidth }) => {
return `calc(328px - 104px - ${maxWidth}px - ${tokenNameWidthList}px)`;
max-width: ${({ containerWidth, tokenNameWidthList, maxWidth }) => {
return `calc(${containerWidth}px - 104px - ${maxWidth}px - ${tokenNameWidthList}px)`;
}};
}
}
.token-path {
.path {
overflow: hidden;
text-overflow: ellipsis;
direction: rtl;
white-space: nowrap;
}
}
`;
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useAtom } from "jotai";
import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
import React, { useCallback, useMemo, useRef } from "react";
import { useTranslation } from "react-i18next";

import Badge, { BADGE_TYPE } from "@components/common/badge/Badge";
Expand All @@ -25,6 +25,8 @@ import {
SearchWrapper,
TokenInfoWrapper,
} from "./SearchMenuModal.styles";
import useElementWidth from "@hooks/common/use-element-width";
import useElementWidthList from "@hooks/common/use-element-width-list";

interface NegativeStatusType {
status: MATH_NEGATIVE_TYPE;
Expand Down Expand Up @@ -77,23 +79,51 @@ const SearchMenuModal: React.FC<SearchMenuModalProps> = ({
const { t } = useTranslation();

const { getGnoscanUrl, getTokenUrl } = useGnoscanUrl();

const [, setRecentsData] = useAtom(TokenState.recents);
const [widthListPopular, setWidthListPopular] = useState<number[]>(popularTokens.map(() => 0));
const [widthListRecent, setWidthListRecent] = useState<number[]>(recents.map(() => 0));
const [tokenNameRecentWidthList, setTokenNameRecentWidthList] = useState<number[]>(recents.map(() => 0));
const [tokenNamePopularWidthList, setTokenNamePopularWidthList] = useState<number[]>(popularTokens.map(() => 0));

const menuRef = useRef<HTMLDivElement | null>(null);

const popularTokenKey = useMemo(() => popularTokens.map(token => token.path).join(","), [popularTokens]);

const recentKey = useMemo(() => recents.map(token => token.path).join(","), [recents]);

const containerRef = useRef<HTMLDivElement | null>(null);
const tokenNamePopularRef = useRef(popularTokens.map(() => React.createRef<HTMLSpanElement>()));
const tokenNameRecentsRef = useRef(recents.map(() => React.createRef<HTMLSpanElement>()));
const recentPriceRef = useRef(recents.map(() => React.createRef<HTMLDivElement>()));
const popularPriceRef = useRef(popularTokens.map(() => React.createRef<HTMLDivElement>()));

const menuRef = useRef<HTMLDivElement | null>(null);
const widthListPopular = useElementWidthList(popularTokens, popularPriceRef.current, [popularPriceRef]);
const widthListRecent = useElementWidthList(recents, recentPriceRef.current, [
recentPriceRef,
popularTokenKey,
keyword,
]);
const tokenNameRecentWidthList = useElementWidthList(recents, tokenNameRecentsRef.current, [
tokenNameRecentsRef,
recentKey,
keyword,
]);
const tokenNamePopularWidthList = useElementWidthList(popularTokens, tokenNamePopularRef.current, [
tokenNamePopularRef,
keyword,
popularTokenKey,
]);

const popularTokenKey = useMemo(() => popularTokens.map(token => token.path).join(","), [popularTokens]);
const containerWidth = useElementWidth(containerRef, [
tokens,
recents,
popularPriceRef,
recentPriceRef,
popularTokenKey,
keyword,
popularTokens,
tokenNameRecentsRef,
recentKey,
tokenNamePopularRef,
popularTokenKey,
]);

const recentKey = useMemo(() => recents.map(token => token.path).join(","), [recents]);
const onClickItem = (item: Token) => {
const current = recents.length > 0 ? [item, recents[0]] : [item];

Expand Down Expand Up @@ -136,71 +166,15 @@ const SearchMenuModal: React.FC<SearchMenuModalProps> = ({
[getGnoscanUrl, getTokenUrl],
);

useEffect(() => {
const widthValues: number[] = [];
popularPriceRef.current.forEach(ref => {
if (ref.current) {
const width = ref.current.getBoundingClientRect().width;
widthValues.push(width);
}
});
setWidthListPopular(widthValues);
}, [popularPriceRef]);

useEffect(() => {
const widthValues: number[] = [];
recentPriceRef.current.forEach(ref => {
if (ref.current) {
const width = ref.current.getBoundingClientRect().width;
widthValues.push(width);
}
});
setWidthListRecent(widthValues);
}, [recentPriceRef, popularTokenKey, keyword]);

useEffect(() => {
const widthValues: number[] = [];
tokenNameRecentsRef.current.forEach(ref => {
if (ref.current) {
const width = ref.current.getBoundingClientRect().width;
widthValues.push(width);
}
});
setTokenNameRecentWidthList(widthValues);
}, [tokenNameRecentsRef, recentKey, keyword]);

useEffect(() => {
const temp: number[] = [];
tokenNamePopularRef.current.forEach(ref => {
if (ref.current) {
const width = ref.current.getBoundingClientRect().width;
temp.push(width);
}
});
setTokenNamePopularWidthList(temp);
}, [tokenNamePopularRef, keyword, popularTokenKey]);

const length = useMemo(() => {
return breakpoint === DEVICE_TYPE.MOBILE ? 15 : 25;
}, [breakpoint]);

const getTokenPathDisplay = useCallback(
(path: string, isNative?: boolean) => {
const path_ = path;

if (isNative) return STATIC_TEXT.NATIVE_COIN;

const tokenPathArr = path_?.split("/") ?? [];

if (tokenPathArr?.length <= 0) return path_;

const replacedPath = path_.replace("gno.land", "");

if (replacedPath.length >= length) {
return "..." + replacedPath.slice(replacedPath.length - length, replacedPath.length - 1);
}

return path_.replace("gno.land", "...");
return path;
},
[length],
);
Expand All @@ -221,7 +195,7 @@ const SearchMenuModal: React.FC<SearchMenuModalProps> = ({
<IconSearch className="search-icon" />
</SearchWrapper>
</SearchContainer>
<ModalContainer>
<ModalContainer ref={containerRef}>
<ul>
{popularTokens.length === 0 && mostLiquidity.length === 0 && isFetched && (
<div className="no-data-found">{t("common:noDataFound")}</div>
Expand All @@ -244,6 +218,7 @@ const SearchMenuModal: React.FC<SearchMenuModalProps> = ({
/>
<TokenInfoWrapper
className="coin-info-detail"
containerWidth={containerWidth}
maxWidth={widthListRecent[idx]}
tokenNameWidthList={tokenNameRecentWidthList[idx]}
>
Expand All @@ -259,7 +234,7 @@ const SearchMenuModal: React.FC<SearchMenuModalProps> = ({
onClickPath(e, item.token.path)
}
>
<div>{getTokenPathDisplay(item.token.path, item.isNative)}</div>
<div className="path">{getTokenPathDisplay(item.token.path, item.isNative)}</div>
<IconNewTab />
</div>
</div>
Expand Down Expand Up @@ -322,6 +297,7 @@ const SearchMenuModal: React.FC<SearchMenuModalProps> = ({
/>
<TokenInfoWrapper
className="coin-info-detail"
containerWidth={containerWidth}
maxWidth={widthListPopular[idx]}
tokenNameWidthList={tokenNamePopularWidthList[idx]}
>
Expand Down
32 changes: 32 additions & 0 deletions packages/web/src/components/common/icons/IconWallet.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { useTheme } from "@emotion/react";

const IconWallet = () => {
const theme = useTheme();
return (
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="12" viewBox="0 0 14 12" fill="none">
<path
d="M12.9987 7.57254H10.7487C9.92069 7.57254 9.24869 6.86854 9.24869 6.00112C9.24869 5.13369 9.92069 4.42969 10.7487 4.42969H12.9987"
stroke={theme.themeKey === "dark" ? "#596782" : "#90a2c0"}
strokeWidth="0.8"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M4 0.5H10C11.656 0.5 13 1.908 13 3.64286V8.35714C13 10.092 11.656 11.5 10 11.5H4C2.344 11.5 1 10.092 1 8.35714V3.64286C1 1.908 2.344 0.5 4 0.5Z"
stroke={theme.themeKey === "dark" ? "#596782" : "#90a2c0"}
strokeWidth="0.8"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M3.99869 3.64062H6.99869"
stroke={theme.themeKey === "dark" ? "#596782" : "#90a2c0"}
strokeWidth="0.8"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
);
};

export default IconWallet;
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,27 @@ import { fonts } from "@constants/font.constant";
import styled from "@emotion/styled";
import { media } from "@styles/media";

export const LineGraphWrapper = styled.div`
export const LineGraphWrapper = styled.div<{ forcedHeight?: number }>`
position: relative;
display: flex;
flex-direction: column;
width: 100%;
height: 321px;
height: ${({ forcedHeight }) => (forcedHeight ? `${forcedHeight}px` : "321px")};
overflow: visible;
${media.mobile} {
height: 252px;
height: ${({ forcedHeight }) => (forcedHeight ? `${forcedHeight}px` : "252px")};
}
& svg {
display: flex;
flex-direction: column;
width: 100%;
height: 321px;
height: ${({ forcedHeight }) => (forcedHeight ? `${forcedHeight}px` : "321px")};
overflow: visible;
.center-line {
transform: translateY(50%) !important;
}
${media.mobile} {
height: 252px;
height: ${({ forcedHeight }) => (forcedHeight ? `${forcedHeight}px` : "252px")};
}
.y-axis-number {
${fonts.p6}
Expand Down
Loading

0 comments on commit 5039e83

Please sign in to comment.