diff --git a/src/components/common/modal/index.css b/src/components/common/modal/index.css index fe4aaa1..93ead0e 100644 --- a/src/components/common/modal/index.css +++ b/src/components/common/modal/index.css @@ -12,15 +12,17 @@ background-color: #fff; border-radius: 5px; box-shadow: 0 1px 5px rgba(0, 0, 0, 0.2); - left: 50%; max-height: 90%; /* min-width: 700px; */ max-width: 90%; overflow: auto; padding: 20px; position: fixed; + + /* transform makes text blurry, so deprecated it */ + /* left: 50%; top: 50%; - transform: translate(-50%, -50%); + transform: translate(-50%, -50%); */ z-index: 999; } diff --git a/src/components/common/modal/index.tsx b/src/components/common/modal/index.tsx index 46db765..b45f987 100644 --- a/src/components/common/modal/index.tsx +++ b/src/components/common/modal/index.tsx @@ -1,6 +1,7 @@ import { useEffect, useState } from "react"; import "./index.css"; +import { useWindowSize } from "../../../utils/hook"; const App = (props: { visible: boolean; @@ -9,6 +10,33 @@ const App = (props: { }) => { const [isModalOpen, setIsModalOpen] = useState(props.visible); + const [offsetTop, setOffsetTop] = useState(0); + const [offsetLeft, setOffsetLeft] = useState(0); + + const usize = useWindowSize(); + + useEffect(() => setTopAndLeft(usize), [usize]); + useEffect(() => setTopAndLeft(usize), [isModalOpen]); + + function setTopAndLeft(winSize: { width?: number; height?: number }) { + const width = winSize.width ?? 800; + const height = winSize.height ?? 800; + + // get modal by className + + const modal = document.querySelector(".modal"); + if (modal) { + const modalWidth = modal.clientWidth; + const modalHeight = modal.clientHeight; + + const top = (height - modalHeight) / 2; + const left = (width - modalWidth) / 2; + + setOffsetTop(top); + setOffsetLeft(left); + } + } + useEffect(() => { setIsModalOpen(props.visible); }, [props.visible]); @@ -35,6 +63,8 @@ const App = (props: { onClick={(e) => e.stopPropagation()} style={{ backgroundColor: "#f5f5f5", + top: offsetTop, + left: offsetLeft, }} > {props.children} diff --git a/src/components/historical-data/index.css b/src/components/historical-data/index.css index 8664503..ff3003e 100644 --- a/src/components/historical-data/index.css +++ b/src/components/historical-data/index.css @@ -62,3 +62,10 @@ right: 0; color: gray; } + +.historical-data-detail-row { + display: inline-block; + height: 40px; + font-size: 17px; + line-height: 40px; +} diff --git a/src/components/historical-data/index.tsx b/src/components/historical-data/index.tsx index c5da819..5abfcb8 100644 --- a/src/components/historical-data/index.tsx +++ b/src/components/historical-data/index.tsx @@ -14,11 +14,13 @@ import { timestampToDate } from "../../utils/date"; import { currencyWrapper, prettyNumberToLocaleString, + prettyPriceNumberToLocaleString, } from "../../utils/currency"; import Modal from "../common/modal"; import { downloadCoinLogos } from "../../middlelayers/data"; import { appCacheDir as getAppCacheDir } from "@tauri-apps/api/path"; import { convertFileSrc } from "@tauri-apps/api/tauri"; +import { useWindowSize } from "../../utils/hook"; type RankData = { id: number; @@ -42,6 +44,8 @@ const App = ({ const [isModalOpen, setIsModalOpen] = useState(false); const [appCacheDir, setAppCacheDir] = useState(""); + const wsize = useWindowSize(); + const [pageNum, setPageNum] = useState(1); const pageSize = 10; @@ -266,18 +270,51 @@ const App = ({ const filePath = `${appCacheDir}assets/coins/${d.symbol.toLowerCase()}.png`; const apiPath = convertFileSrc(filePath); return ( -
- {d.symbol} -
{d.rank}
-
{d.symbol}
-
{d.amount}
-
{d.value}
-
{d.price}
-
+ + +

{d.rank}

+ + + {d.symbol} +

{d.symbol}

+ + +

+ {currency.symbol + prettyPriceNumberToLocaleString(currencyWrapper(currency)(d.price))} +

+ + +

{d.amount}

+ + +

+ {currency.symbol + + prettyNumberToLocaleString( + currencyWrapper(currency)(d.value) + )} +

+ + ); }) .value(); @@ -297,12 +334,56 @@ const App = ({ id="detail-view" style={{ display: rankData.length > 0 ? "inline-block" : "none", - marginRight: 10, - verticalAlign: "top", + width: (wsize.width ?? 800) * 0.7, }} > - {/* */} - {detailPage(rankData)} +
+ + + + + + + + + + {detailPage(rankData)} +
+ # + + Name + + Price + + Amount + + Value +
= 1, keep 3 digits after decimal point +export function prettyPriceNumberToLocaleString(value: number) { + if (value >= 1) { + return prettyNumberToLocaleString(value) + } + + // find last 0, and keep 3 digits after last 0 + const str = safeNumberToString(value) + // +2 because of "0." at begin + let last0 = 2 + for (let i = 0; i < str.length; i++) { + if (str[i] === ".") { + continue + } + if (str[i] !== "0") { + last0 = i + break + } + } + return str.substring(0, last0 + 4) +} + +// safe transfer number to string without scientific notation +function safeNumberToString(inputNumber: number) { + const inum = parseFloat('' + inputNumber) + let eformat = inum.toExponential() + const tmpArray = eformat.match(/\d(?:\.(\d*))?e([+-]\d+)/) + if (!tmpArray) { + return inputNumber + '' + } + let number = inputNumber.toFixed(Math.max(0, (tmpArray[1] || '').length - parseInt(tmpArray[2]))) + return number +} \ No newline at end of file