From 66226eedb180f6de56bf0298d5b1eb5e2b07323c Mon Sep 17 00:00:00 2001 From: madhead Date: Fri, 27 Oct 2023 00:01:37 +0200 Subject: [PATCH] Transaction component --- .../src/common/transaction/Transaction.less | 92 ++++++++++++++++++ .../src/common/transaction/Transaction.tsx | 94 +++++++++++++++++++ 2 files changed, 186 insertions(+) create mode 100644 mini-app/src/common/transaction/Transaction.less create mode 100644 mini-app/src/common/transaction/Transaction.tsx diff --git a/mini-app/src/common/transaction/Transaction.less b/mini-app/src/common/transaction/Transaction.less new file mode 100644 index 00000000..faf3c639 --- /dev/null +++ b/mini-app/src/common/transaction/Transaction.less @@ -0,0 +1,92 @@ +.transaction { + display: flex; + flex-direction: column; + margin: 16px; + + & > .info { + display: flex; + flex-direction: row; + + & > .timestamp { + display: flex; + flex-direction: column; + + color: var(--tg-theme-hint-color); + text-align: justify; + text-justify: auto; + min-width: 40px; + margin-inline-end: 8px; + + @media (pointer: coarse), (hover: none) { + &::after { + content: ""; + } + &:hover::after { + content: attr(title); + position: absolute; + z-index: 2; + background-color: var(--tg-theme-bg-color); + box-shadow: 0px 3px 5px -1px rgba(0, 0, 0, 0.2), + 0px 6px 10px 0px rgba(0, 0, 0, 0.14), + 0px 1px 18px 0px rgba(0, 0, 0, 0.12); + padding: 0 8px; + height: 36pt; + line-height: 36pt; + font-size: 16pt; + transition: 0.1s; + } + } + + & > .month { + font-size: 12pt; + line-height: 12pt; + text-transform: uppercase; + } + + & > .date { + font-size: 24pt; + line-height: 24pt; + } + } + + & > .title { + display: flex; + flex-direction: column-reverse; + font-size: 18pt; + flex-grow: 1; + margin-inline: 8px 8px; + max-height: 36pt; + overflow: hidden; + + .spacer { + display: block; + width: 25vw; + } + } + + & > .amount { + display: flex; + flex-direction: column; + + color: var(--tg-theme-link-color); + text-align: end; + + margin-inline-start: 8px; + + & > .currency { + font-size: 12pt; + line-height: 12pt; + text-transform: uppercase; + } + + & > .quantity { + font-size: 24pt; + line-height: 24pt; + } + } + } + + & > .participants { + display: none; + } +} diff --git a/mini-app/src/common/transaction/Transaction.tsx b/mini-app/src/common/transaction/Transaction.tsx new file mode 100644 index 00000000..168810a3 --- /dev/null +++ b/mini-app/src/common/transaction/Transaction.tsx @@ -0,0 +1,94 @@ +import moment from "moment"; +import { useEffect, useRef, useState } from "react"; +import Marquee from "react-fast-marquee"; +import "./Transaction.less"; + +export type Transaction = { + id: number; + payer: number; + recipients: Set; + amount: string; + currency: string; + title: string; + timestamp: number; +}; + +export default function TransactionCard(transaction: Transaction) { + return ( +
+
+ + + <Amount amount={transaction.amount} currency={transaction.currency} /> + </div> + <div className="participants"></div> + </div> + ); +} + +function Timestamp({ timestamp }: { timestamp: number }) { + const m = moment(timestamp); + + return ( + <div className="timestamp" title={m.format("llll")}> + <div className="month">{m.format("MMM")}</div> + <div className="date">{m.format("DD")}</div> + </div> + ); +} + +function Title({ title }: { title: string }) { + const titleContainerRef = useRef<HTMLDivElement>(null); + const [marquee, setMarquee] = useState(false); + + useEffect(() => { + if (!titleContainerRef.current) { + return; + } + + const element = titleContainerRef.current; + + if ( + element.offsetWidth < element.scrollWidth || + element.offsetHeight < element.scrollHeight + ) { + setMarquee(true); + // element.classList.add("marquee"); + } + }, [titleContainerRef]); + + return ( + <div className="title" ref={titleContainerRef}> + {marquee && ( + <Marquee delay={3}> + {title} + <span className="spacer" /> + </Marquee> + )} + {!marquee && <span>{title}</span>} + </div> + ); +} + +function Amount({ amount, currency }: { amount: string; currency: string }) { + function formatAmount(amount: string) { + let result = parseFloat(amount).toFixed(2); + + while (result[result.length - 1] === "0") { + result = result.slice(0, result.length - 1); + } + + if (result[result.length - 1] === ".") { + result = result.slice(0, result.length - 1); + } + + return result; + } + + return ( + <div className="amount"> + <div className="currency">{currency}</div> + <div className="quantity">{formatAmount(amount)}</div> + </div> + ); +}