-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add MultiStat component, add feature flag for apr and roi switching (#…
- Loading branch information
1 parent
24965f6
commit 91d13a1
Showing
7 changed files
with
299 additions
and
44 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
98 changes: 98 additions & 0 deletions
98
apps/hyperdrive-trading/src/ui/base/components/MultiStat.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
import { InformationCircleIcon } from "@heroicons/react/24/outline"; | ||
import classNames from "classnames"; | ||
import { ReactElement } from "react"; | ||
import { Stat, StatProps } from "src/ui/base/components/Stat"; | ||
import { useActiveItem } from "src/ui/base/hooks/useActiveItem"; | ||
|
||
export interface MultiStatProps extends StatProps { | ||
id: string; | ||
} | ||
|
||
export function MultiStat({ | ||
stats, | ||
activeStatId, | ||
onTabChange, | ||
}: { | ||
stats: MultiStatProps[]; | ||
onTabChange: (stat: MultiStatProps) => void; | ||
activeStatId: string; | ||
}): ReactElement { | ||
const { activeItem, setActiveItemId } = useActiveItem({ | ||
items: stats, | ||
idField: "id", | ||
defaultActiveItemId: stats.find((stat) => activeStatId === stat.id) | ||
?.id as string, | ||
}); | ||
|
||
return ( | ||
<button | ||
className="flex flex-col gap-2" | ||
onClick={() => { | ||
// if the active item isn't the last one, go to the next item | ||
const currentStatIndex = stats.findIndex((i) => i.id === activeItem.id); | ||
let nextStat = stats[0]; | ||
if (currentStatIndex !== stats.length - 1) { | ||
nextStat = stats[currentStatIndex + 1]; | ||
} | ||
setActiveItemId(nextStat.id); | ||
onTabChange(nextStat); | ||
}} | ||
> | ||
<Stat | ||
label={ | ||
<div className="flex flex-col gap-1"> | ||
{/* Implements the same label as the Stat component, but adds the | ||
tab buttons below the label. This is done so that the tabs can be | ||
centered under the label, not the whole Stat element */} | ||
{activeItem.description ? ( | ||
<p | ||
data-tip={activeItem.description} | ||
className={classNames( | ||
`group daisy-tooltip cursor-help text-start text-sm text-neutral-content before:z-40 before:max-w-56 before:p-2 before:text-start`, | ||
{ | ||
"daisy-tooltip-top": activeItem.tooltipPosition === "top", | ||
"daisy-tooltip-bottom": | ||
activeItem.tooltipPosition === "bottom", | ||
"daisy-tooltip-left": activeItem.tooltipPosition === "left", | ||
"daisy-tooltip-right": | ||
activeItem.tooltipPosition === "right", | ||
}, | ||
)} | ||
> | ||
{activeItem.label} | ||
<InformationCircleIcon className="group-hover:text-gray-500 ml-1 hidden w-4 text-neutral-content opacity-0 transition duration-150 ease-in-out group-hover:opacity-100 lg:inline-block" /> | ||
</p> | ||
) : ( | ||
<p className="self-start text-sm text-neutral-content"> | ||
{activeItem.label} | ||
</p> | ||
)} | ||
|
||
<div | ||
className={classNames("flex items-center justify-center gap-2", { | ||
"pr-4": !!activeItem.description, | ||
})} | ||
> | ||
{stats.map(({ id }) => { | ||
return ( | ||
<div | ||
key={id} | ||
className={classNames( | ||
"transition-all hover:opacity-90", | ||
id === activeItem.id | ||
? "scale-150 text-base-content" | ||
: "text-neutral-content hover:scale-125", | ||
)} | ||
> | ||
• | ||
</div> | ||
); | ||
})} | ||
</div> | ||
</div> | ||
} | ||
value={activeItem.value} | ||
/> | ||
</button> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
66 changes: 66 additions & 0 deletions
66
apps/hyperdrive-trading/src/ui/markets/MarketStats/FixedRateStat.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
import { HyperdriveConfig } from "@hyperdrive/appconfig"; | ||
import classNames from "classnames"; | ||
import { ReactElement } from "react"; | ||
import Skeleton from "react-loading-skeleton"; | ||
import { useLocalStorage } from "react-use"; | ||
import { MultiStat, MultiStatProps } from "src/ui/base/components/MultiStat"; | ||
import { useIsTailwindSmallScreen } from "src/ui/base/mediaBreakpoints"; | ||
import { useCurrentFixedAPR } from "src/ui/hyperdrive/hooks/useCurrentFixedAPR"; | ||
|
||
export function FixedRateStat({ | ||
hyperdrive, | ||
}: { | ||
hyperdrive: HyperdriveConfig; | ||
}): ReactElement { | ||
const isTailwindSmallScreen = useIsTailwindSmallScreen(); | ||
const { fixedAPR, fixedAPRStatus } = useCurrentFixedAPR(hyperdrive.address); | ||
const [rateType, setRateType] = useLocalStorage<"fixedApr" | "fixedRoi">( | ||
"yield-stats-long-rate-type", | ||
"fixedApr", | ||
); | ||
return ( | ||
<MultiStat | ||
activeStatId={ | ||
rateType! /* Stripping off the undefined because we set a default value | ||
in useLocalStorage */ | ||
} | ||
stats={[ | ||
{ | ||
id: "fixedApr", | ||
label: "Fixed APR", | ||
value: | ||
fixedAPRStatus === "loading" && fixedAPR === undefined ? ( | ||
<Skeleton className="w-20" /> | ||
) : ( | ||
<span className={classNames("flex items-center gap-1.5")}> | ||
{fixedAPR?.formatted || "0"}% | ||
</span> | ||
), | ||
|
||
description: | ||
"Annualized fixed rate earned from opening longs, before fees and slippage are applied.", | ||
tooltipPosition: isTailwindSmallScreen ? "right" : "bottom", | ||
}, | ||
{ | ||
id: "fixedRoi", | ||
label: "Fixed ROI", | ||
value: | ||
fixedAPRStatus === "loading" && fixedAPR === undefined ? ( | ||
<Skeleton className="w-20" /> | ||
) : ( | ||
<span className={classNames("flex items-center gap-1.5")}> | ||
{fixedAPR?.formatted || "0"}% | ||
</span> | ||
), | ||
|
||
description: | ||
"Holding period return for the duration of the term, before fees and slippage are applied.", | ||
tooltipPosition: isTailwindSmallScreen ? "right" : "bottom", | ||
}, | ||
]} | ||
onTabChange={(stat: MultiStatProps) => { | ||
setRateType(stat.id as any); | ||
}} | ||
/> | ||
); | ||
} |
80 changes: 80 additions & 0 deletions
80
apps/hyperdrive-trading/src/ui/markets/MarketStats/ShortRateStat.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
import { HyperdriveConfig } from "@hyperdrive/appconfig"; | ||
import classNames from "classnames"; | ||
import { ReactElement } from "react"; | ||
import Skeleton from "react-loading-skeleton"; | ||
import { useLocalStorage } from "react-use"; | ||
import { formatRate } from "src/base/formatRate"; | ||
import { parseUnits } from "src/base/parseUnits"; | ||
import { MultiStat, MultiStatProps } from "src/ui/base/components/MultiStat"; | ||
import { useImpliedRate } from "src/ui/hyperdrive/shorts/hooks/useImpliedRate"; | ||
import { useYieldSourceRate } from "src/ui/vaults/useYieldSourceRate"; | ||
|
||
export function ShortRateStat({ | ||
hyperdrive, | ||
}: { | ||
hyperdrive: HyperdriveConfig; | ||
}): ReactElement { | ||
const [rateType, setRateType] = useLocalStorage<"shortApr" | "shortRoi">( | ||
"yield-stats-short-rate-type", | ||
"shortApr", | ||
); | ||
const { vaultRate } = useYieldSourceRate({ | ||
hyperdriveAddress: hyperdrive.address, | ||
}); | ||
|
||
const { impliedRate, impliedRateStatus, impliedRateFetchStatus } = | ||
useImpliedRate({ | ||
bondAmount: parseUnits("1", 18), | ||
hyperdriveAddress: hyperdrive.address, | ||
variableApy: vaultRate?.vaultRate ? vaultRate.vaultRate : undefined, | ||
timestamp: BigInt(Math.floor(Date.now() / 1000)), | ||
}); | ||
const isLoadingShortRoi = | ||
impliedRateStatus === "loading" && | ||
impliedRateFetchStatus === "fetching" && | ||
impliedRate === undefined; | ||
|
||
const formattedRate = impliedRate ? `${formatRate(impliedRate)}%` : "-"; | ||
|
||
return ( | ||
<MultiStat | ||
activeStatId={ | ||
rateType! /* Stripping off the undefined because we set a default value | ||
in useLocalStorage */ | ||
} | ||
stats={[ | ||
{ | ||
id: "shortApr", | ||
label: "Short APR", | ||
description: | ||
"Annualized return on shorts assuming the current variable rate stays the same for 1 year.", | ||
tooltipPosition: "bottom", | ||
value: isLoadingShortRoi ? ( | ||
<Skeleton className="w-20" /> | ||
) : ( | ||
<span className={classNames("flex items-center gap-1.5")}> | ||
{formattedRate} | ||
</span> | ||
), | ||
}, | ||
{ | ||
id: "shortRoi", | ||
label: "Short ROI", | ||
description: | ||
"Holding period return on shorts assuming the current variable rate stays the same until maturity.", | ||
tooltipPosition: "bottom", | ||
value: isLoadingShortRoi ? ( | ||
<Skeleton className="w-20" /> | ||
) : ( | ||
<span className={classNames("flex items-center gap-1.5")}> | ||
{formattedRate} | ||
</span> | ||
), | ||
}, | ||
]} | ||
onTabChange={(stat: MultiStatProps) => { | ||
setRateType(stat.id as any); | ||
}} | ||
/> | ||
); | ||
} |
Oops, something went wrong.