Skip to content
Merged
22 changes: 22 additions & 0 deletions apps/insights/src/components/PriceCard/index.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
@use "@pythnetwork/component-library/theme";

.feedCardContents {
align-items: stretch;
display: flex;
flex-flow: column nowrap;
gap: theme.spacing(6);
justify-content: space-between;
padding: theme.spacing(3);
}

.prices {
align-items: center;
color: theme.color("heading");
display: flex;
flex-flow: row nowrap;
font-size: theme.font-size("base");
font-weight: theme.font-weight("medium");
gap: theme.spacing(2);
justify-content: space-between;
line-height: 1;
}
68 changes: 68 additions & 0 deletions apps/insights/src/components/PriceCard/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { Card } from "@pythnetwork/component-library/Card";
import { SymbolPairTag } from "@pythnetwork/component-library/SymbolPairTag";
import cx from "clsx";
import type { PropsWithChildren, Ref } from "react";

import styles from "./index.module.scss";
import { PriceFeedIcon } from "../PriceFeedIcon";

type PriceCardProps = PropsWithChildren & {
/**
* if specified, will display a different
* icon to denote the asset.
* otherwise, a default icon will be used.
*/
assetClass?: string;

/**
* css class name override
*/
className?: string;

/**
* additional description or useful
* content to be displayed underneath
* the displaySymbol name
*/
description?: string;

/**
* human-friendly shortname for the symbol
* being visualized
*/
displaySymbol: string;

/**
* if provided, will be passed to the underlying <Card />,
* which will turn it into a clickable hyperlink
*/
href?: string;

/**
* ref handle
*/
ref?: Ref<HTMLDivElement>;
};

export function PriceCard({
assetClass,
children,
className,
description,
displaySymbol,
href,
ref,
}: PriceCardProps) {
return (
<Card href={href} ref={ref} variant="tertiary">
<div className={cx(styles.feedCardContents, className)}>
<SymbolPairTag
displaySymbol={displaySymbol}
description={description ?? ""}
icon={<PriceFeedIcon assetClass={assetClass ?? ""} />}
/>
{children && <div className={styles.prices}>{children}</div>}
</div>
</Card>
);
}
26 changes: 3 additions & 23 deletions apps/insights/src/components/PriceFeeds/index.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -32,30 +32,10 @@
& > * {
min-width: 0;
}
}

.feedCardContents {
display: flex;
flex-flow: column nowrap;
justify-content: space-between;
align-items: stretch;
padding: theme.spacing(3);
gap: theme.spacing(6);

.prices {
display: flex;
flex-flow: row nowrap;
justify-content: space-between;
align-items: center;
color: theme.color("heading");
font-weight: theme.font-weight("medium");
line-height: 1;
font-size: theme.font-size("base");

.changePercent {
font-size: theme.font-size("sm");
}
}
}
.changePercent {
font-size: theme.font-size("sm");
}

.bigScreenBody {
Expand Down
43 changes: 19 additions & 24 deletions apps/insights/src/components/PriceFeeds/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import { Button } from "@pythnetwork/component-library/Button";
import type { Props as CardProps } from "@pythnetwork/component-library/Card";
import { Card } from "@pythnetwork/component-library/Card";
import { StatCard } from "@pythnetwork/component-library/StatCard";
import { SymbolPairTag } from "@pythnetwork/component-library/SymbolPairTag";
import { TabList } from "@pythnetwork/component-library/TabList";
import {
TabPanel as UnstyledTabPanel,
Expand All @@ -26,6 +25,7 @@ import { priceFeeds as priceFeedsStaticConfig } from "../../static-data/price-fe
import { activeChains } from "../../static-data/stats";
import { Cards } from "../Cards";
import { LivePrice } from "../LivePrices";
import { PriceCard } from "../PriceCard";
import {
YesterdaysPricesProvider,
PriceFeedChangePercent,
Expand Down Expand Up @@ -239,31 +239,26 @@ const FeaturedFeedsCard = <T extends ElementType>({
<Card {...props}>
<div className={styles.featuredFeedsCard}>
{feeds.map((feed) => (
<Card
key={feed.product.price_account}
variant="tertiary"
<PriceCard
assetClass={feed.product.asset_type}
description={feed.product.description}
displaySymbol={feed.product.display_symbol}
href={`/price-feeds/${encodeURIComponent(feed.symbol)}`}
key={feed.product.price_account}
>
<div className={styles.feedCardContents}>
<SymbolPairTag
displaySymbol={feed.product.display_symbol}
description={feed.product.description}
icon={<PriceFeedIcon assetClass={feed.product.asset_type} />}
/>
{showPrices && (
<div className={styles.prices}>
<LivePrice
feedKey={feed.product.price_account}
cluster={Cluster.Pythnet}
/>
<PriceFeedChangePercent
className={styles.changePercent}
feedKey={feed.product.price_account}
/>
</div>
)}
</div>
</Card>
{showPrices && (
<>
<LivePrice
feedKey={feed.product.price_account}
cluster={Cluster.Pythnet}
/>
<PriceFeedChangePercent
className={styles.changePercent}
feedKey={feed.product.price_account}
/>
</>
)}
</PriceCard>
))}
</div>
</Card>
Expand Down
49 changes: 15 additions & 34 deletions apps/insights/src/components/PythFeedsDemoPage/index.module.scss
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
@use "@pythnetwork/component-library/theme";

.body {
padding-top: theme.spacing(4);
background-color: theme.color("background", "secondary");
border-radius: theme.border-radius("2xl");
display: flex;
flex-flow: column;
gap: theme.spacing(2);
min-height: theme.spacing(80);
padding: theme.spacing(2);
}

.suspenseLoader {
@include theme.max-width;

align-items: center;
display: flex;
flex-flow: column;
Expand All @@ -19,47 +26,21 @@
}

.subheader {
border-bottom: 1px solid theme.color("border");
display: flex;
flex-flow: column;
gap: theme.spacing(4);
padding-bottom: theme.spacing(4);
gap: theme.spacing(2);
margin-bottom: theme.spacing(4);

@include theme.breakpoint("sm") {
align-items: flex-end;
flex-flow: initial;
justify-content: space-between;
& > h3,
& > h4 {
margin: 0;
}

& > div {
&:first-child {
display: flex;
flex-flow: column;
gap: theme.spacing(4);

& > h3,
& > h4 {
margin: 0;
}

& > h4 {
color: theme.color("muted");
}
}

&:last-child {
align-items: center;
display: flex;
gap: theme.spacing(2);
}
& > h4 {
color: theme.color("muted");
}
}

.pythFeedsDemoPageRoot {
@include theme.max-width;

// this holds the chart
& > aside {
margin-top: theme.spacing(2);
}
}
43 changes: 21 additions & 22 deletions apps/insights/src/components/PythFeedsDemoPage/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,30 @@ import {
PythProAppStateProvider,
WebSocketsProvider,
} from "../../context/pyth-pro-demo";
import { PythProApiTokensMenu } from "../PythProApiTokensMenu";
import { PythProDemoCards } from "../PythProDemoCards";
import { PythProDemoPriceChart } from "../PythProDemoPriceChart";
import { PythProDemoSourceSelector } from "../PythProDemoSourceSelector";
import { PythProDemoToolsMenu } from "../PythProDemoToolsMenu";

function PythFeedsDemoPageImpl() {
return (
<article className={classes.pythFeedsDemoPageRoot}>
<section>
<div className={classes.subheader}>
<h3>Pyth Pro</h3>
<h4>Real-time feed comparison tool</h4>
</div>
<div className={classes.body}>
<PythProDemoToolsMenu />
<PythProDemoCards />
<PythProDemoPriceChart />
</div>
</section>
</article>
);
}

export function PythFeedsDemoPage() {
/** local variables */
const suspenseLoaderLabel = "Initializing Pyth Pro demo...";

return (
Expand All @@ -29,26 +47,7 @@ export function PythFeedsDemoPage() {
>
<PythProAppStateProvider>
<WebSocketsProvider>
<article className={classes.pythFeedsDemoPageRoot}>
<section>
<div className={classes.subheader}>
<div>
<h3>Pyth Pro</h3>
<h4>Real-time feed comparison tool</h4>
</div>
<div>
<PythProApiTokensMenu />
<PythProDemoSourceSelector />
</div>
</div>
<div className={classes.body}>
<PythProDemoCards />
</div>
</section>
<aside>
<PythProDemoPriceChart />
</aside>
</article>
<PythFeedsDemoPageImpl />
</WebSocketsProvider>
</PythProAppStateProvider>
</Suspense>
Expand Down
20 changes: 12 additions & 8 deletions apps/insights/src/components/PythProApiTokensMenu/index.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,22 @@
import { GearSix, X } from "@phosphor-icons/react/dist/ssr";
import type { Props as ButtonProps } from "@pythnetwork/component-library/Button";
import { Button } from "@pythnetwork/component-library/Button";
import { Input } from "@pythnetwork/component-library/Input";
import { ModalDialog } from "@pythnetwork/component-library/ModalDialog";
import { Tooltip } from "@pythnetwork/component-library/Tooltip";
import { sentenceCase } from "change-case";
import { useState } from "react";
import { Label, Tooltip, TooltipTrigger } from "react-aria-components";
import { Label } from "react-aria-components";

import classes from "./index.module.scss";
import { usePythProApiTokensContext } from "../../context/pyth-pro-demo";
import { DATA_SOURCES_REQUIRING_API_TOKENS } from "../../schemas/pyth/pyth-pro-demo-schema";

export function PythProApiTokensMenu() {
type PythProApiTokensMenuProps = Partial<Pick<ButtonProps<never>, "variant">>;

export function PythProApiTokensMenu({
variant = "outline",
}: PythProApiTokensMenuProps) {
/** context */
const { tokens, updateApiToken } = usePythProApiTokensContext();

Expand All @@ -23,20 +29,18 @@ export function PythProApiTokensMenu() {

return (
<>
<TooltipTrigger delay={0}>
<Tooltip delay={0} label={tooltip}>
<Button
aria-label={tooltip}
onClick={() => {
setOpen(true);
}}
variant="outline"
size="sm"
variant={variant}
>
<GearSix />
</Button>
<Tooltip className={classes.tooltip ?? ""} placement="bottom">
{tooltip}
</Tooltip>
</TooltipTrigger>
</Tooltip>
<ModalDialog
className={classes.modal ?? ""}
isOpen={open}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
@use "@pythnetwork/component-library/theme";

.root {
font-size: 2rem;
gap: theme.spacing(2);
margin-top: theme.spacing(4);
align-items: center;
display: flex;
flex-grow: 1;
font-size: theme.font-size("2xl");
margin: 0 auto;
justify-content: center;
max-width: theme.spacing(110);
text-align: center;
}
Loading
Loading