Skip to content

Commit ce19a00

Browse files
committed
feat: wp'
1 parent 676607e commit ce19a00

6 files changed

Lines changed: 395 additions & 93 deletions

File tree

src/app/token/[tokenId]/PageClient.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { isSBTC } from '@/app/tokens/utils';
55
import { Sip10Alert } from '@/app/txid/[txId]/redesign/Alert';
66
import { Grid, Stack } from '@chakra-ui/react';
77

8-
import { SbtcFaq } from './redesign/SbtcFAQ';
8+
import { SbtcFaq } from './redesign/SbtcFaq';
99
import { TokenAlert } from './redesign/TokenAlert';
1010
import { TokenIdHeader } from './redesign/TokenIdHeader';
1111
import { TokenIdTabs } from './redesign/TokenIdTabs';
@@ -31,10 +31,10 @@ export default function TokenIdPageRedesign() {
3131
<TokenIdHeader />
3232
<TokenStats />
3333
</Stack>
34-
<Stack gap={8}>
34+
<Grid templateRows={'1fr auto'} gap={2} h="full">
3535
<TotalSupplyCard />
3636
<SbtcFaq />
37-
</Stack>
37+
</Grid>
3838
</Grid>
3939
<TokenAlert />
4040
<TokenIdTabs />

src/app/token/[tokenId]/redesign/TokenStats.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -157,14 +157,14 @@ function TokenStatsCardContent({
157157
{title}
158158
</Text>
159159
{typeof value === 'string' ? (
160-
<Text textStyle="text-medium-2xl" color="textPrimary" whiteSpace="nowrap">
160+
<Text textStyle="text-medium-2xl" color="textPrimary">
161161
{value}
162162
</Text>
163163
) : (
164164
value
165165
)}
166166
{secondaryValue && (
167-
<Text textStyle="text-medium-xs" color="textSecondary" whiteSpace="nowrap">
167+
<Text textStyle="text-medium-xs" color="textSecondary">
168168
{secondaryValue}
169169
</Text>
170170
)}

src/common/components/ReverseAccordion.tsx

Lines changed: 97 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,19 @@
1-
"use client";
1+
'use client';
22

3-
import useResizeObserver from '@/common/hooks/useResizeObserver';
3+
import { useResizeObserver } from '@/common/hooks/useResizeObserver';
44
import { Button } from '@/ui/Button';
55
import { Text } from '@/ui/Text';
66
import { Box, Flex, Icon, Stack } from '@chakra-ui/react';
77
import { Plus, X } from '@phosphor-icons/react';
8-
import { RefObject, createRef, useCallback, useEffect, useMemo, useRef, useState } from 'react';
8+
import { createRef, useCallback, useEffect, useMemo, useRef, useState } from 'react';
99

10-
const cardPaddingY = 3;
11-
const cardMarginBottom = 3;
12-
const cardOverlap = 10; // Amount of overlap between cards
13-
const onHoverRise = 40; // How much the card rises when hovered
14-
const extraRiseToShowContent = cardOverlap - 5;
15-
16-
const calculateCardHeight = (cardTitleHeight: number) => {
17-
return cardTitleHeight + cardMarginBottom * 4 + cardPaddingY * 2 * 4;
18-
};
10+
const DEFUALT_ITEM_HEIGHT_IN_PX = 40; // can make this custom
11+
const DEFAULT_ITEM_OVERLAP_IN_PX = 12;
12+
const DEFAULT_PX = 4;
13+
const DEFAULT_PY = 3;
14+
const DEFAULT_HOVER_RISE_IN_PX = 40; // How much the card rises when hovered
15+
const EXTRA_RISE_TO_SHOW_CONTENT_IN_PX = DEFAULT_ITEM_OVERLAP_IN_PX / 2;
16+
const MARGIN_FROM_TOP_IN_PX = 20;
1917

2018
function ReverseAccordionItem({
2119
title,
@@ -25,9 +23,11 @@ function ReverseAccordionItem({
2523
index,
2624
setIsExpanded,
2725
isExpanded,
28-
itemTitleRef,
29-
topPosition,
3026
accordionWidth,
27+
cardHeightInPx,
28+
top,
29+
px,
30+
py,
3131
}: {
3232
title: string;
3333
text: string;
@@ -36,103 +36,126 @@ function ReverseAccordionItem({
3636
index: number;
3737
setIsExpanded: (index: number, state: boolean) => void;
3838
isExpanded: boolean;
39-
itemTitleRef: RefObject<HTMLDivElement>;
40-
topPosition: number;
4139
accordionWidth: number;
40+
cardHeightInPx: number;
41+
top: number;
42+
px?: number;
43+
py?: number;
4244
}) {
4345
const [isHovered, setIsHovered] = useState(false);
4446
const [contentHeight, setContentHeight] = useState(0);
45-
const [titleHeight, setTitleHeight] = useState(0);
4647
const contentRef = useRef<HTMLDivElement>(null);
4748
const containerRef = useRef<HTMLDivElement>(null);
48-
const cardHeight = useMemo(() => calculateCardHeight(titleHeight), [titleHeight]);
49+
const [rectTop, setRectTop] = useState<number>(0);
50+
const maxRise = useMemo(
51+
() => rectTop - cardHeightInPx - MARGIN_FROM_TOP_IN_PX,
52+
[rectTop, cardHeightInPx]
53+
);
4954

55+
// Sets the content height
5056
useEffect(() => {
5157
if (contentRef.current) {
5258
setContentHeight(contentRef.current.scrollHeight);
59+
if (contentRef.current.getBoundingClientRect().top > rectTop) {
60+
setRectTop(contentRef.current.getBoundingClientRect().top);
61+
}
5362
}
54-
}, [text, isExpanded, isHovered, accordionWidth]);
55-
56-
useEffect(() => {
57-
if (itemTitleRef.current) {
58-
setTitleHeight(itemTitleRef.current.scrollHeight);
59-
}
60-
}, [text, isExpanded, isHovered, accordionWidth]);
63+
}, [text, isExpanded, isHovered, accordionWidth, rectTop]);
6164

6265
return (
6366
<Stack
6467
className="CARD"
6568
ref={containerRef}
66-
gap={0}
6769
position="absolute"
68-
top={`${topPosition}px`}
70+
top={`${top}px`}
6971
left={0}
7072
right={0}
71-
bg="white"
72-
border="1px solid var(--stacks-colors-sand-200)"
73+
gap={0}
74+
bg="surfaceFourth"
75+
border="1px solid var(--stacks-colors-sand-50)"
7376
borderRadius="xl"
7477
onMouseEnter={() => setIsHovered(true)}
7578
onMouseLeave={() => setIsHovered(false)}
7679
onClick={() => (isExpanded ? null : setIsExpanded(index, true))}
77-
style={{
78-
// the transform is there to balance out the height so that the card will look like it moves upward instead of downward. For example, if the height grows by 20px, we can move the card up 20px so that the height drops back down to the original bottom of the card
79-
transition: 'all 0.3s ease-out',
80-
transform: isExpanded
81-
? `translateY(-${contentHeight + extraRiseToShowContent}px)`
80+
transition="all 0.3s ease-out"
81+
transform={
82+
isExpanded
83+
? `translateY(-${Math.min(contentHeight, maxRise) + EXTRA_RISE_TO_SHOW_CONTENT_IN_PX}px)`
8284
: isHovered
83-
? `translateY(-${onHoverRise}px)`
84-
: 'translateY(0)',
85-
height: isExpanded
86-
? `${cardHeight + contentHeight}px`
85+
? `translateY(-${DEFAULT_HOVER_RISE_IN_PX}px)`
86+
: 'translateY(0)'
87+
}
88+
height={
89+
isExpanded
90+
? `${cardHeightInPx + Math.min(contentHeight, maxRise)}px`
8791
: isHovered
88-
? `${cardHeight + onHoverRise}px`
89-
: `${cardHeight}px`,
90-
overflow: 'hidden',
91-
zIndex: 10 + index,
92-
}}
92+
? `${cardHeightInPx + DEFAULT_HOVER_RISE_IN_PX}px`
93+
: `${cardHeightInPx}px`
94+
}
95+
overflow="hidden"
96+
zIndex={1000 + index}
9397
boxShadow="0px -8px 10px -6px rgba(0, 0, 0, 0.1)"
9498
>
95-
<Stack gap={0}>
96-
<Flex height={cardHeight} className="CARD-TITLE-CONTAINER" alignItems="center" px={4}>
97-
<Flex
98-
justifyContent="space-between"
99-
alignItems="center"
100-
className="cardTitle"
101-
ref={itemTitleRef}
102-
height="fit-content"
103-
w="full"
99+
<>
100+
<Flex
101+
className="CARD-TITLE"
102+
height={`${cardHeightInPx}px`}
103+
alignItems="center"
104+
justifyContent="space-between"
105+
w="full"
106+
px={px}
107+
py={py}
108+
>
109+
<Text textStyle="text-medium-xs">{title}</Text>
110+
<Icon
111+
onClick={() => (isExpanded ? setIsExpanded(index, false) : null)}
112+
h={4}
113+
w={4}
114+
color="iconTertiary"
104115
>
105-
<Text fontWeight="medium">{title}</Text>
106-
<Icon onClick={() => (isExpanded ? setIsExpanded(index, false) : null)} h={4} w={4}>
107-
{isExpanded ? <X /> : <Plus />}
108-
</Icon>
109-
</Flex>
116+
{isExpanded ? <X /> : <Plus />}
117+
</Icon>
110118
</Flex>
111-
<Flex ref={contentRef} height="fit-content" flexDirection="column" gap={4} px={3} pb={3}>
119+
<Stack className="CARD-CONTENT" ref={contentRef} gap={4} px={px} pb={py} overflowY="scroll">
112120
<Text color="textSubdued">{text}</Text>
113121
<Button>{linkLabel}</Button>
114-
</Flex>
115-
</Stack>
122+
</Stack>
123+
</>
116124
</Stack>
117125
);
118126
}
119127

120128
export interface ReverseAccordionItem {
121-
// This should be made more generic to accomodate other types of item content
122129
title: string;
123130
text: string;
124131
link: string;
125132
linkLabel: string;
126133
}
127134

128-
export function ReverseAccordion({ items }: { items: ReverseAccordionItem[] }) {
129-
const [totalHeight, setTotalHeight] = useState(0);
135+
function calculateReverseAccordionHeight(
136+
numOfItems: number,
137+
itemHeight: number,
138+
itemOverlap: number
139+
) {
140+
return `${numOfItems * itemHeight - itemOverlap * (numOfItems - 1)}px`;
141+
}
142+
143+
export function ReverseAccordion({
144+
items,
145+
itemHeight = DEFUALT_ITEM_HEIGHT_IN_PX,
146+
itemOverlap = DEFAULT_ITEM_OVERLAP_IN_PX,
147+
}: {
148+
items: ReverseAccordionItem[];
149+
itemHeight?: number;
150+
itemOverlap?: number;
151+
}) {
152+
const [totalHeightInPx, _] = useState(
153+
calculateReverseAccordionHeight(items.length, itemHeight, itemOverlap)
154+
);
130155
const containerRef = useRef<HTMLDivElement>(null);
131156
const [expandedIndex, setExpandedIndex] = useState<number | null>(null);
132-
const itemTitleRefs = useRef<Array<React.RefObject<HTMLDivElement>>>(
133-
Array(items.length)
134-
.fill(null)
135-
.map(() => createRef<HTMLDivElement>())
157+
const itemTitleRefs = useRef<React.RefObject<HTMLDivElement | null>[]>(
158+
Array.from({ length: items.length }, () => createRef<HTMLDivElement>())
136159
);
137160

138161
const setIsExpanded = useCallback((index: number, state: boolean) => {
@@ -145,21 +168,10 @@ export function ReverseAccordion({ items }: { items: ReverseAccordionItem[] }) {
145168

146169
const { width } = useResizeObserver(containerRef);
147170

148-
useEffect(() => {
149-
if (containerRef.current) {
150-
const height =
151-
itemTitleRefs.current.reduce(
152-
(sum, ref) => sum + (calculateCardHeight(ref.current?.scrollHeight ?? 0) - cardOverlap),
153-
0
154-
) + cardOverlap; // add the overlap back on to account for the last card
155-
setTotalHeight(height);
156-
}
157-
}, [items, width]);
158-
159171
return (
160172
<Box
161173
position="relative"
162-
height={`${totalHeight}px`}
174+
height={totalHeightInPx}
163175
w="full"
164176
ref={containerRef}
165177
className="REVERSE-ACCORDION"
@@ -174,15 +186,13 @@ export function ReverseAccordion({ items }: { items: ReverseAccordionItem[] }) {
174186
index={index}
175187
setIsExpanded={setIsExpanded}
176188
isExpanded={expandedIndex === index}
177-
itemTitleRef={itemTitleRefs.current[index]}
178-
topPosition={itemTitleRefs.current
179-
.slice(0, index)
180-
.reduce(
181-
(sum, ref) =>
182-
sum + (calculateCardHeight(ref.current?.scrollHeight ?? 0) - cardOverlap),
183-
0
184-
)}
185189
accordionWidth={width}
190+
cardHeightInPx={itemHeight}
191+
top={itemTitleRefs.current // This controls the positioning of the cards, accounting for the overlap between cards
192+
.slice(0, index)
193+
.reduce((sum, ref) => sum + (itemHeight - itemOverlap), 0)}
194+
px={DEFAULT_PX}
195+
py={DEFAULT_PY}
186196
/>
187197
))}
188198
</Box>

src/common/hooks/useResizeObserver.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ interface Size {
55
height: number;
66
}
77

8-
export function useResizeObserver(ref: React.RefObject<HTMLElement>): Size {
8+
export function useResizeObserver(ref: React.RefObject<HTMLElement | null>): Size {
99
const [size, setSize] = useState<Size>({ width: 0, height: 0 });
1010
const observerRef = useRef<ResizeObserver | null>(null);
1111

0 commit comments

Comments
 (0)