diff --git a/apps/insights/.env.development b/apps/insights/.env.development new file mode 100644 index 0000000000..addc9dd5a0 --- /dev/null +++ b/apps/insights/.env.development @@ -0,0 +1 @@ +DISABLE_ACCESSIBILITY_REPORTING=true \ No newline at end of file diff --git a/apps/insights/.gitignore b/apps/insights/.gitignore index 9d2ee2a739..e6fc78b8ba 100644 --- a/apps/insights/.gitignore +++ b/apps/insights/.gitignore @@ -1 +1,2 @@ .env*.local +.env*.development \ No newline at end of file diff --git a/apps/insights/src/components/CardTitle/index.module.scss b/apps/insights/src/components/CardTitle/index.module.scss new file mode 100644 index 0000000000..ccb19f4b02 --- /dev/null +++ b/apps/insights/src/components/CardTitle/index.module.scss @@ -0,0 +1,25 @@ +@use "@pythnetwork/component-library/theme"; + +.cardTitle { + display: flex; + flex-flow: row nowrap; + gap: theme.spacing(3); + align-items: center; + justify-content: flex-start; + .title { + color: theme.color("heading"); + display: flex; + flex-flow: row nowrap; + gap: theme.spacing(3); + align-items: center; + @include theme.text("base", "semibold"); + @include theme.breakpoint("md") { + @include theme.text("lg", "semibold"); + } + } + .icon { + font-size: theme.spacing(6); + height: theme.spacing(6); + color: theme.color("button", "primary", "background", "normal"); + } +} diff --git a/apps/insights/src/components/CardTitle/index.tsx b/apps/insights/src/components/CardTitle/index.tsx new file mode 100644 index 0000000000..902235689e --- /dev/null +++ b/apps/insights/src/components/CardTitle/index.tsx @@ -0,0 +1,20 @@ +import clsx from "clsx"; +import type { ComponentProps, ReactNode } from "react"; + +import styles from "./index.module.scss"; + +type CardTitleProps = { + children: ReactNode; + icon?: ReactNode | undefined; + badge?: ReactNode; +} & ComponentProps<"div">; + +export const CardTitle = ({ children, icon, badge, ...props }: CardTitleProps) => { + return ( +
+ {icon &&
{icon}
} +

{children}

+ {badge} +
+ ) +} \ No newline at end of file diff --git a/apps/insights/src/components/CopyButton/index.tsx b/apps/insights/src/components/CopyButton/index.tsx index f002ccbbbc..53de57de10 100644 --- a/apps/insights/src/components/CopyButton/index.tsx +++ b/apps/insights/src/components/CopyButton/index.tsx @@ -27,7 +27,6 @@ export const CopyButton = ({ text, children, className, ...props }: Props) => { const [isCopied, setIsCopied] = useState(false); const logger = useLogger(); const copy = useCallback(() => { - // eslint-disable-next-line n/no-unsupported-features/node-builtins navigator.clipboard .writeText(text) .then(() => { diff --git a/apps/insights/src/components/MobileMenu/mobile-menu.module.scss b/apps/insights/src/components/MobileMenu/mobile-menu.module.scss new file mode 100644 index 0000000000..ae47132655 --- /dev/null +++ b/apps/insights/src/components/MobileMenu/mobile-menu.module.scss @@ -0,0 +1,54 @@ +@use "@pythnetwork/component-library/theme"; + +.mobileMenuTrigger { + display: block; + + @include theme.breakpoint("md") { + display: none; + } +} + +.mobileMenuOverlay { + background: rgb(0 0 0 / 40%); + position: fixed; + inset: 0; + z-index: 999; +} + +.mobileMenuContainer { + border-top-left-radius: theme.border-radius("2xl"); + border-top-right-radius: theme.border-radius("2xl"); + background: theme.color("background", "modal"); + position: absolute; + bottom: 0; + left: 0; + right: 0; + padding: 1rem; + display: flex; + flex-flow: column nowrap; + gap: theme.spacing(4); +} + +.mobileMenuHandle { + background: theme.color("background", "secondary"); + width: 33%; + height: 6px; + border-radius: theme.border-radius("full"); + align-self: center; +} + +.mobileThemeSwitcher { + display: flex; + flex-flow: row nowrap; + justify-content: space-between; + align-items: center; +} + +.mobileThemeSwitcherFeedback { + display: flex; + flex-flow: row nowrap; + align-items: center; + gap: theme.spacing(3); + text-transform: capitalize; + font-weight: theme.font-weight("medium"); +} diff --git a/apps/insights/src/components/MobileMenu/mobile-menu.tsx b/apps/insights/src/components/MobileMenu/mobile-menu.tsx new file mode 100644 index 0000000000..4e5932b425 --- /dev/null +++ b/apps/insights/src/components/MobileMenu/mobile-menu.tsx @@ -0,0 +1,29 @@ +"use client"; +import { List } from "@phosphor-icons/react/dist/ssr/List"; +import { Button } from "@pythnetwork/component-library/Button"; +import clsx from "clsx"; +import { useState, type ComponentProps } from "react"; + +import styles from "./mobile-menu.module.scss"; + +export const MobileMenu = ({ className, ...props }: ComponentProps<"div">) => { + const [isOpen, setIsOpen] = useState(false); + + const toggleMenu = () => { + setIsOpen(!isOpen); + }; + + return ( +
+ +
+ ); +}; diff --git a/apps/insights/src/components/MobileNavigation/mobile-navigation.module.scss b/apps/insights/src/components/MobileNavigation/mobile-navigation.module.scss new file mode 100644 index 0000000000..00e1c7ceec --- /dev/null +++ b/apps/insights/src/components/MobileNavigation/mobile-navigation.module.scss @@ -0,0 +1,12 @@ +@use "@pythnetwork/component-library/theme"; + +.mobileNavigation { + display: block; + padding: theme.spacing(2); + background: theme.color("background", "primary"); + border-top: 1px solid theme.color("background", "secondary"); + + @include theme.breakpoint("md") { + display: none; + } +} diff --git a/apps/insights/src/components/MobileNavigation/mobile-navigation.tsx b/apps/insights/src/components/MobileNavigation/mobile-navigation.tsx new file mode 100644 index 0000000000..0ee7773a4b --- /dev/null +++ b/apps/insights/src/components/MobileNavigation/mobile-navigation.tsx @@ -0,0 +1,9 @@ +import styles from "./mobile-navigation.module.scss"; +import { MainNavTabs } from "../Root/tabs"; +export const MobileNavigation = () => { + return ( +
+ +
+ ); +}; diff --git a/apps/insights/src/components/Overview/index.tsx b/apps/insights/src/components/Overview/index.tsx index 3185cbd16d..1d9bf083d2 100644 --- a/apps/insights/src/components/Overview/index.tsx +++ b/apps/insights/src/components/Overview/index.tsx @@ -1,7 +1,9 @@ -import styles from "./index.module.scss"; +import { Card } from "@pythnetwork/component-library/Card"; + +import { PageLayout } from "../PageLayout/page-layout"; export const Overview = () => ( -
-

Overview

-
+ + + ); diff --git a/apps/insights/src/components/PageLayout/page-layout.module.scss b/apps/insights/src/components/PageLayout/page-layout.module.scss new file mode 100644 index 0000000000..442bb61b8e --- /dev/null +++ b/apps/insights/src/components/PageLayout/page-layout.module.scss @@ -0,0 +1,24 @@ +@use "@pythnetwork/component-library/theme"; + +.pageLayout { + @include theme.max-width; + display: flex; + gap: theme.spacing(6); + flex-direction: column; + + .pageTitleContainer { + display: flex; + flex-flow: row nowrap; + gap: theme.spacing(3); + width: 100%; + align-items: center; + justify-content: space-between; + + .pageTitle { + @include theme.h3; + color: theme.color("heading"); + font-weight: theme.font-weight("semibold"); + flex-grow: 1; + } + } +} diff --git a/apps/insights/src/components/PageLayout/page-layout.tsx b/apps/insights/src/components/PageLayout/page-layout.tsx new file mode 100644 index 0000000000..b70e04c255 --- /dev/null +++ b/apps/insights/src/components/PageLayout/page-layout.tsx @@ -0,0 +1,15 @@ +import type { ReactNode } from "react"; + +import styles from "./page-layout.module.scss"; + +export const PageLayout = ({ children, title, actions }: { children: ReactNode; title: ReactNode, actions?: ReactNode }) => { + return ( +
+
+

{title}

+ {actions &&
{actions}
} +
+ {children} +
+ ) +} \ No newline at end of file diff --git a/apps/insights/src/components/PriceFeedTag/index.module.scss b/apps/insights/src/components/PriceFeedTag/index.module.scss index 0ca6b99f12..fc92baf48e 100644 --- a/apps/insights/src/components/PriceFeedTag/index.module.scss +++ b/apps/insights/src/components/PriceFeedTag/index.module.scss @@ -1,8 +1,8 @@ @use "@pythnetwork/component-library/theme"; .priceFeedTag { - display: flex; - flex-flow: row nowrap; + display: grid; + grid-template-columns: theme.spacing(10) 1fr; gap: theme.spacing(3); align-items: center; @@ -13,6 +13,8 @@ } .nameAndDescription { + width: 100%; + position: relative; display: flex; flex-flow: column nowrap; gap: theme.spacing(1.5); @@ -52,6 +54,8 @@ color: theme.color("muted"); overflow: hidden; text-overflow: ellipsis; + white-space: nowrap; // Add this line + width: 100%; @include theme.text("xs", "medium"); } diff --git a/apps/insights/src/components/PriceFeeds/index.module.scss b/apps/insights/src/components/PriceFeeds/index.module.scss index d5927a894b..dd081b90e7 100644 --- a/apps/insights/src/components/PriceFeeds/index.module.scss +++ b/apps/insights/src/components/PriceFeeds/index.module.scss @@ -1,66 +1,53 @@ @use "@pythnetwork/component-library/theme"; -.priceFeeds { - @include theme.max-width; +.toolbarContainer { + display: flex; + flex-flow: row nowrap; + gap: theme.spacing(2); +} + +.feedKey { + margin: 0 -#{theme.button-padding("xs", true)}; +} - .header { - @include theme.h3; +.featuredFeeds { + display: flex; + flex-flow: column nowrap; + align-items: stretch; - color: theme.color("heading"); - font-weight: theme.font-weight("semibold"); + @include theme.breakpoint("md") { + flex-flow: row nowrap; + + & > * { + flex: 1 1 0px; + width: 0; + } } +} + +.featuredFeeds { + gap: theme.spacing(1); - .body { + .feedCardContents { display: flex; flex-flow: column nowrap; + justify-content: space-between; + align-items: stretch; + padding: theme.spacing(3); gap: theme.spacing(6); - margin-top: theme.spacing(6); - - .feedKey { - margin: 0 -#{theme.button-padding("xs", true)}; - } - .featuredFeeds, - .stats { + .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"); - & > * { - flex: 1 1 0px; - width: 0; - } - } - - .stats { - gap: theme.spacing(6); - } - - .featuredFeeds { - gap: theme.spacing(1); - - .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"); } } } diff --git a/apps/insights/src/components/PriceFeeds/index.tsx b/apps/insights/src/components/PriceFeeds/index.tsx index 16691c0844..05f5d1f35a 100644 --- a/apps/insights/src/components/PriceFeeds/index.tsx +++ b/apps/insights/src/components/PriceFeeds/index.tsx @@ -19,10 +19,13 @@ import styles from "./index.module.scss"; import { PriceFeedsCard } from "./price-feeds-card"; import { Cluster, getData } from "../../services/pyth"; import { priceFeeds as priceFeedsStaticConfig } from "../../static-data/price-feeds"; +import { CardTitle } from "../CardTitle"; import { YesterdaysPricesProvider, ChangePercent } from "../ChangePercent"; import { LivePrice } from "../LivePrices"; +import { PageLayout } from "../PageLayout/page-layout"; import { PriceFeedIcon } from "../PriceFeedIcon"; import { PriceFeedTag } from "../PriceFeedTag"; +import { Stats } from "../Stats"; const PRICE_FEEDS_ANCHOR = "priceFeeds"; @@ -45,99 +48,94 @@ export const PriceFeeds = async () => { ); return ( -
-

Price Feeds

-
-
- } - /> - + + + } + /> + + } + /> + } + header="Asset Classes" + stat={Object.keys(numFeedsByAssetClass).length} + corner={} /> - - } - /> - -
- [ - symbol, - product.price_account, - ]), - )} - > - } - feeds={featuredRecentlyAdded} - showPrices - linkFeeds - /> - + + + [ + symbol, + product.price_account, + ]), + )} + > } - feeds={featuredComingSoon} - toolbar={ - - - - Coming Soon - {priceFeeds.comingSoon.length} - - } - > - ({ - id: feed.product.price_account, - displaySymbol: feed.product.display_symbol, - assetClass: feed.product.asset_type, - icon: ( - - ), - }))} - /> - - - } - /> - ({ - symbol: feed.symbol, - icon: , - id: feed.product.price_account, - displaySymbol: feed.product.display_symbol, - assetClass: feed.product.asset_type, - exponent: feed.price.exponent, - numQuoters: feed.price.numQuoters, - }))} + title={}>Recently added} + feeds={featuredRecentlyAdded} + showPrices + linkFeeds /> -
-
+ + }>Coming soon} + feeds={featuredComingSoon} + action={ + + + + Coming Soon + {priceFeeds.comingSoon.length} + + } + > + ({ + id: feed.product.price_account, + displaySymbol: feed.product.display_symbol, + assetClass: feed.product.asset_type, + icon: ( + + ), + }))} + /> + + + } + /> + ({ + symbol: feed.symbol, + icon: , + id: feed.product.price_account, + displaySymbol: feed.product.display_symbol, + assetClass: feed.product.asset_type, + exponent: feed.price.exponent, + numQuoters: feed.price.numQuoters, + }))} + /> + ); }; diff --git a/apps/insights/src/components/PriceFeeds/price-feed-items.module.scss b/apps/insights/src/components/PriceFeeds/price-feed-items.module.scss new file mode 100644 index 0000000000..9825c9e6e4 --- /dev/null +++ b/apps/insights/src/components/PriceFeeds/price-feed-items.module.scss @@ -0,0 +1,22 @@ +@use "@pythnetwork/component-library/theme"; + +.priceFeedItemsWrapper { + display: flex; + flex-flow: column nowrap; + gap: 0; + border-radius: theme.border-radius("xl"); + background: theme.color("background", "card-secondary"); + + @include theme.breakpoint("md") { + display: none; + } +} + +.priceFeedItem { + padding: theme.spacing(4); + position: relative; + + &:not(:last-child) { + border-bottom: 1px solid theme.color("background", "secondary"); + } +} diff --git a/apps/insights/src/components/PriceFeeds/price-feed-items.tsx b/apps/insights/src/components/PriceFeeds/price-feed-items.tsx new file mode 100644 index 0000000000..3759311ca7 --- /dev/null +++ b/apps/insights/src/components/PriceFeeds/price-feed-items.tsx @@ -0,0 +1,32 @@ +import styles from "./price-feed-items.module.scss"; +import { PriceFeedTag } from "../PriceFeedTag"; +import { StructuredList } from "../StructuredList"; + +export const PriceFeedItems = () => { + return ( +
+ {Array.from({ length: 20 }).map((_, index) => { + return ( +
+ , + value: "$32,323.22", + }, + { + label: "Last Price", + value: "$10,000.00", + }, + { + label: "Last Updated", + value: "2022-01-01", + }, + ]} + /> +
+ ); + })} +
+ ); +}; diff --git a/apps/insights/src/components/PriceFeeds/price-feeds-card.tsx b/apps/insights/src/components/PriceFeeds/price-feeds-card.tsx index 0fe94882fd..3d632740ed 100644 --- a/apps/insights/src/components/PriceFeeds/price-feeds-card.tsx +++ b/apps/insights/src/components/PriceFeeds/price-feeds-card.tsx @@ -16,7 +16,9 @@ import { useQueryState, parseAsString } from "nuqs"; import { type ReactNode, Suspense, useCallback, useMemo } from "react"; import { useFilter, useCollator } from "react-aria"; +import { PriceFeedItems } from "./price-feed-items"; import { useQueryParamFilterPagination } from "../../use-query-param-filter-pagination"; +import { CardTitle } from "../CardTitle"; import { FeedKey } from "../FeedKey"; import { SKELETON_WIDTH, @@ -195,49 +197,56 @@ type PriceFeedsCardContents = Pick & ( | { isLoading: true } | { - isLoading?: false; - numResults: number; - search: string; - sortDescriptor: SortDescriptor; - onSortChange: (newSort: SortDescriptor) => void; - assetClass: string; - assetClasses: string[]; - numPages: number; - page: number; - pageSize: number; - onSearchChange: (newSearch: string) => void; - onAssetClassChange: (newAssetClass: string) => void; - onPageSizeChange: (newPageSize: number) => void; - onPageChange: (newPage: number) => void; - mkPageLink: (page: number) => string; - rows: RowConfig< - | "priceFeedName" - | "assetClass" - | "priceFeedId" - | "price" - | "confidenceInterval" - | "exponent" - | "numPublishers" - >[]; - } + isLoading?: false; + numResults: number; + search: string; + sortDescriptor: SortDescriptor; + onSortChange: (newSort: SortDescriptor) => void; + assetClass: string; + assetClasses: string[]; + numPages: number; + page: number; + pageSize: number; + onSearchChange: (newSearch: string) => void; + onAssetClassChange: (newAssetClass: string) => void; + onPageSizeChange: (newPageSize: number) => void; + onPageChange: (newPage: number) => void; + mkPageLink: (page: number) => string; + rows: RowConfig< + | "priceFeedName" + | "assetClass" + | "priceFeedId" + | "price" + | "confidenceInterval" + | "exponent" + | "numPublishers" + >[]; + } ); const PriceFeedsCardContents = ({ id, ...props }: PriceFeedsCardContents) => ( } title={ - <> - Price Feeds - {!props.isLoading && ( - - {props.numResults} - - )} - + } badge={!props.isLoading && ( + + {props.numResults} + + )}>Price Feeds } toolbar={ <> + label="Asset Class" size="sm" @@ -246,29 +255,18 @@ const PriceFeedsCardContents = ({ id, ...props }: PriceFeedsCardContents) => ( {...(props.isLoading ? { isPending: true, options: [], buttonLabel: "Asset Class" } : { - optionGroups: [ - { name: "All", options: [""] }, - { name: "Asset classes", options: props.assetClasses }, - ], - hideGroupLabel: true, - show: (value) => (value === "" ? "All" : value), - placement: "bottom end", - buttonLabel: - props.assetClass === "" ? "Asset Class" : props.assetClass, - selectedKey: props.assetClass, - onSelectionChange: props.onAssetClassChange, - })} - /> - (value === "" ? "All" : value), + placement: "bottom end", + buttonLabel: + props.assetClass === "" ? "Asset Class" : props.assetClass, + selectedKey: props.assetClass, + onSelectionChange: props.onAssetClassChange, + })} /> } @@ -286,6 +284,8 @@ const PriceFeedsCardContents = ({ id, ...props }: PriceFeedsCardContents) => ( ), })} > + + ( ]} {...(props.isLoading ? { - isLoading: true, - } + isLoading: true, + } : { - rows: props.rows, - sortDescriptor: props.sortDescriptor, - onSortChange: props.onSortChange, - emptyState: ( - { - props.onSearchChange(""); - }} - /> - ), - })} + rows: props.rows, + sortDescriptor: props.sortDescriptor, + onSortChange: props.onSortChange, + emptyState: ( + { + props.onSearchChange(""); + }} + /> + ), + })} /> ); diff --git a/apps/insights/src/components/Publisher/layout.module.scss b/apps/insights/src/components/Publisher/layout.module.scss index 81697a4ec2..be96824372 100644 --- a/apps/insights/src/components/Publisher/layout.module.scss +++ b/apps/insights/src/components/Publisher/layout.module.scss @@ -84,8 +84,7 @@ .body { @include theme.max-width; - - padding-top: theme.spacing(6); + margin-top: theme.spacing(6); } } diff --git a/apps/insights/src/components/Publisher/layout.tsx b/apps/insights/src/components/Publisher/layout.tsx index fc7cfc7bae..55df87dd79 100644 --- a/apps/insights/src/components/Publisher/layout.tsx +++ b/apps/insights/src/components/Publisher/layout.tsx @@ -40,6 +40,7 @@ import { PublisherKey } from "../PublisherKey"; import { PublisherTag } from "../PublisherTag"; import { ScoreHistory } from "../ScoreHistory"; import { SemicircleMeter } from "../SemicircleMeter"; +import { Stats } from "../Stats"; import { TabPanel, TabRoot, Tabs } from "../Tabs"; import { TokenIcon } from "../TokenIcon"; @@ -110,264 +111,266 @@ export const PublishersLayout = async ({ children, params }: Props) => { />
- - - }> -

- Each Publisher receives a Ranking which is - derived from the number of price feeds the{" "} - Publisher is actively publishing. -

-
- - } - data={rankingHistory.map(({ timestamp, rank }) => ({ - x: timestamp, - y: rank, - displayX: ( - - - - ), - }))} - stat={currentRanking.rank} - {...(previousRanking && { - miniStat: ( - - {Math.abs(currentRanking.rank - previousRanking.rank)} - - ), - })} - /> - + } - data={medianScoreHistory.map(({ time, score }) => ({ - x: time, - y: score, + variant="primary" + header="Publisher Ranking" + lineClassName={styles.primarySparkChartLine} + corner={ + + + }> +

+ Each Publisher receives a Ranking which is + derived from the number of price feeds the{" "} + Publisher is actively publishing. +

+
+
+ } + data={rankingHistory.map(({ timestamp, rank }) => ({ + x: timestamp, + y: rank, displayX: ( - + ), - displayY: ( - - ), }))} - stat={ - - } - {...(previousMedianScore && { + stat={currentRanking.rank} + {...(previousRanking && { miniStat: ( - - % + {Math.abs(currentRanking.rank - previousRanking.rank)} ), })} /> - - Documentation - - } - > - - } header="Publisher Score"> - Each price feed a publisher provides has an associated score, - which is determined by the component{"'"}s uptime, price - deviation, and staleness. This panel shows the median for each - score across all price feeds published by this publisher, as - well as the overall median score across all those feeds. - - -
- feed.status === Status.Active) - .length - } - totalFeeds={totalFeedsCount} - /> - - oisStats.maxPoolSize - ? "" - : undefined - } - > + + } + data={medianScoreHistory.map(({ time, score }) => ({ + x: time, + y: score, + displayX: ( + + + + ), + displayY: ( + + ), + }))} + stat={ - % - - } - corner={} - > - - - - - - } - endLabel={ - - - - + - - - } + % + + ), + })} /> - - - + Documentation - - } - > - - -
OIS Pool
-
+ + } header="Publisher Score"> + Each price feed a publisher provides has an associated score, + which is determined by the component{"'"}s uptime, price + deviation, and staleness. This panel shows the median for each + score across all price feeds published by this publisher, as + well as the overall median score across all those feeds. + +
+
+ feed.status === Status.Active) + .length + } + totalFeeds={totalFeedsCount} + /> + - - - + oisStats.maxPoolSize + ? "" + : undefined + } + > + + % + } - /> - } + > + + + + + + + } + endLabel={ + + + + + + + } + /> + + - - + + } - /> - - } - header="Oracle Integrity Staking (OIS)" > - OIS allows anyone to help secure Pyth and protect DeFi. - Through decentralized staking rewards and slashing, OIS - incentivizes Pyth publishers to maintain high-quality data - contributions. PYTH holders can stake to publishers to further - reinforce oracle security. Rewards are programmatically - distributed to high quality publishers and the stakers - supporting them to strengthen oracle integrity. - - - + + +
OIS Pool
+
+ + + + + } + /> + + + + + } + /> + + } + header="Oracle Integrity Staking (OIS)" + > + OIS allows anyone to help secure Pyth and protect DeFi. + Through decentralized staking rewards and slashing, OIS + incentivizes Pyth publishers to maintain high-quality data + contributions. PYTH holders can stake to publishers to further + reinforce oracle security. Rewards are programmatically + distributed to high quality publishers and the stakers + supporting them to strengthen oracle integrity. + + + +
diff --git a/apps/insights/src/components/Publisher/performance.module.scss b/apps/insights/src/components/Publisher/performance.module.scss index e5e5fd6bad..32ef3b26f9 100644 --- a/apps/insights/src/components/Publisher/performance.module.scss +++ b/apps/insights/src/components/Publisher/performance.module.scss @@ -2,11 +2,14 @@ .performance { display: grid; - grid-template-columns: 1fr 1fr; - gap: theme.spacing(12) theme.spacing(6); + grid-template-columns: 1fr; + gap: theme.spacing(6); align-items: flex-start; - > *:first-child { - grid-column: span 2 / span 2; + @include theme.breakpoint("md") { + grid-template-columns: 1fr 1fr; + > *:first-child { + grid-column: span 2 / span 2; + } } } diff --git a/apps/insights/src/components/Publisher/performance.tsx b/apps/insights/src/components/Publisher/performance.tsx index 37a629f23c..1d70171bf8 100644 --- a/apps/insights/src/components/Publisher/performance.tsx +++ b/apps/insights/src/components/Publisher/performance.tsx @@ -14,6 +14,7 @@ import { TopFeedsTable } from "./top-feeds-table"; import { getPublishers } from "../../services/clickhouse"; import { Cluster, getTotalFeedCount } from "../../services/pyth"; import { Status } from "../../status"; +import { CardTitle } from "../CardTitle"; import { NoResults } from "../NoResults"; import { PriceFeedIcon } from "../PriceFeedIcon"; import { PriceFeedTag } from "../PriceFeedTag"; @@ -47,7 +48,7 @@ export const Performance = async ({ params }: Props) => { notFound() ) : (
- } title="Publishers Ranking"> + } title={Publisher Ranking}>
{ })} /> - } title="High-Performing Feeds"> + }>High-performing Feeds}> { )} /> - } title="Low-Performing Feeds"> + }>Low-performing Feeds}> { ]); return ( -
-

Publishers

+
- - - - }> -

- Each Price Feed Component that a Publisher{" "} - provides has an associated Score, which is determined - by that component{"'"}s Uptime,{" "} - Price Deviation, and Staleness. The publisher - {"'"}s Median Score measures the 50th percentile of - the Score across all of that publisher{"'"}s{" "} - Price Feed Components. The{" "} - Average Median Score is the average of the{" "} - Median Scores of all publishers who contribute to the - Pyth Network. -

+ + + -
- - } - stat={( - publishers.reduce( - (sum, publisher) => sum + publisher.medianScore, - 0, - ) / publishers.length - ).toFixed(2)} - /> + }> +

+ Each Price Feed Component that a Publisher{" "} + provides has an associated Score, which is determined + by that component{"'"}s Uptime,{" "} + Price Deviation, and Staleness. The publisher + {"'"}s Median Score measures the 50th percentile of + the Score across all of that publisher{"'"}s{" "} + Price Feed Components. The{" "} + Average Median Score is the average of the{" "} + Median Scores of all publishers who contribute to the + Pyth Network. +

+ +
+ + } + stat={( + publishers.reduce( + (sum, publisher) => sum + publisher.medianScore, + 0, + ) / publishers.length + ).toFixed(2)} + /> + OIS} className={styles.oisCard} - toolbar={ + action={
-
+ ); }; diff --git a/apps/insights/src/components/Publishers/publishers-card.tsx b/apps/insights/src/components/Publishers/publishers-card.tsx index 0d421575d3..b0fa44b758 100644 --- a/apps/insights/src/components/Publishers/publishers-card.tsx +++ b/apps/insights/src/components/Publishers/publishers-card.tsx @@ -14,6 +14,7 @@ import { type ReactNode, Suspense, useMemo } from "react"; import { useFilter, useCollator } from "react-aria"; import { useQueryParamFilterPagination } from "../../use-query-param-filter-pagination"; +import { CardTitle } from "../CardTitle"; import { NoResults } from "../NoResults"; import { PublisherTag } from "../PublisherTag"; import { Ranking } from "../Ranking"; @@ -35,9 +36,9 @@ type Publisher = { inactiveFeeds: number; medianScore: number; } & ( - | { name: string; icon: ReactNode } - | { name?: undefined; icon?: undefined } -); + | { name: string; icon: ReactNode } + | { name?: undefined; icon?: undefined } + ); export const PublishersCard = ({ publishers, ...props }: Props) => ( }> @@ -155,22 +156,22 @@ type PublishersCardContentsProps = Pick< ( | { isLoading: true } | { - isLoading?: false; - numResults: number; - search: string; - sortDescriptor: SortDescriptor; - numPages: number; - page: number; - pageSize: number; - onSearchChange: (newSearch: string) => void; - onSortChange: (newSort: SortDescriptor) => void; - onPageSizeChange: (newPageSize: number) => void; - onPageChange: (newPage: number) => void; - mkPageLink: (page: number) => string; - rows: RowConfig< - "ranking" | "name" | "activeFeeds" | "inactiveFeeds" | "medianScore" - >[]; - } + isLoading?: false; + numResults: number; + search: string; + sortDescriptor: SortDescriptor; + numPages: number; + page: number; + pageSize: number; + onSearchChange: (newSearch: string) => void; + onSortChange: (newSort: SortDescriptor) => void; + onPageSizeChange: (newPageSize: number) => void; + onPageChange: (newPage: number) => void; + mkPageLink: (page: number) => string; + rows: RowConfig< + "ranking" | "name" | "activeFeeds" | "inactiveFeeds" | "medianScore" + >[]; + } ); const PublishersCardContents = ({ @@ -180,16 +181,12 @@ const PublishersCardContents = ({ }: PublishersCardContentsProps) => ( } title={ - <> - Publishers - {!props.isLoading && ( - - {props.numResults} - - )} - + + {props.numResults} + + )} icon={}>Publishers } toolbar={ } {...(!props.isLoading && { @@ -264,21 +261,21 @@ const PublishersCardContents = ({ ]} {...(props.isLoading ? { - isLoading: true, - } + isLoading: true, + } : { - rows: props.rows, - sortDescriptor: props.sortDescriptor, - onSortChange: props.onSortChange, - emptyState: ( - { - props.onSearchChange(""); - }} - /> - ), - })} + rows: props.rows, + sortDescriptor: props.sortDescriptor, + onSortChange: props.onSortChange, + emptyState: ( + { + props.onSearchChange(""); + }} + /> + ), + })} /> ); diff --git a/apps/insights/src/components/Root/footer.module.scss b/apps/insights/src/components/Root/footer.module.scss index f6799a3bf8..d28c839e48 100644 --- a/apps/insights/src/components/Root/footer.module.scss +++ b/apps/insights/src/components/Root/footer.module.scss @@ -2,41 +2,32 @@ .footer { // SM - background: theme.color("background", "primary"); - - // XL + margin-top: theme.spacing(8); padding: theme.spacing(8) 0; - - // bg-beige-100 sm:border-t sm:border-stone-300 + background: theme.color("background", "primary"); .topContent { display: flex; gap: theme.spacing(6); - - // SM - flex-flow: row nowrap; - align-items: center; + flex-flow: column nowrap; + align-items: flex-start; justify-content: space-between; @include theme.max-width; - // XL - margin-bottom: theme.spacing(12); - - // py-6 + margin-bottom: theme.spacing(6); - // flex-col + @include theme.breakpoint("sm") { + flex-flow: row nowrap; + align-items: center; + } .left { display: flex; align-items: stretch; justify-content: space-between; - - // SM gap: theme.spacing(6); - // gap-8 - .logoLink { height: theme.spacing(5); box-sizing: content-box; @@ -86,10 +77,12 @@ .bottomContent { display: flex; gap: theme.spacing(6); + flex-flow: column; - // SM - flex-flow: row nowrap; - justify-content: space-between; + @include theme.breakpoint("sm") { + flex-flow: row nowrap; + justify-content: space-between; + } // "flex-col diff --git a/apps/insights/src/components/Root/footer.tsx b/apps/insights/src/components/Root/footer.tsx index 343d49e678..04fa9b773b 100644 --- a/apps/insights/src/components/Root/footer.tsx +++ b/apps/insights/src/components/Root/footer.tsx @@ -1,3 +1,4 @@ +import { ArrowSquareOut } from "@phosphor-icons/react/dist/ssr/ArrowSquareOut"; import { type Props as ButtonProps, Button, @@ -21,10 +22,10 @@ export const Footer = () => (
- Help + Support - Documentation + Documentation
diff --git a/apps/insights/src/components/Root/header.module.scss b/apps/insights/src/components/Root/header.module.scss index 65a3ff8506..fea43a9ca9 100644 --- a/apps/insights/src/components/Root/header.module.scss +++ b/apps/insights/src/components/Root/header.module.scss @@ -4,8 +4,9 @@ position: sticky; top: 0; width: 100%; - background-color: theme.color("background", "nav-blur"); - backdrop-filter: blur(32px); + background-color: theme.color("background", "primary"); + // TODO: This causes that navigation is not fixed + // backdrop-filter: blur(32px); .content { height: 100%; @@ -16,18 +17,24 @@ .leftMenu { flex: none; - gap: theme.spacing(6); + gap: theme.spacing(4); + position: relative; @include theme.row; .logoLink { padding: theme.spacing(3); - margin: -#{theme.spacing(3)}; color: theme.color("foreground"); + position: relative; + + @include theme.breakpoint("3xl") { + position: absolute; + left: -#{theme.spacing(16)}; + } .logoWrapper { - width: theme.spacing(9); - height: theme.spacing(9); + width: theme.spacing(8); + height: theme.spacing(8); position: relative; .logo { @@ -52,33 +59,34 @@ .rightMenu { flex: none; + position: relative; gap: theme.spacing(2); @include theme.row; - margin-right: -#{theme.button-padding("sm", false)}; - .themeSwitch { - margin-left: theme.spacing(1); - } - } - - @media screen and (min-width: theme.$max-width + (2 * (theme.spacing(9) + theme.spacing(8) + theme.spacing(7)))) { - .leftMenu { - margin-left: -#{theme.spacing(9) + theme.spacing(7)}; + display: none; - .logoLink { - margin-right: -#{theme.spacing(2)}; + @include theme.breakpoint("md") { + position: relative; + display: block; } - } - - .rightMenu { - margin-right: -#{theme.spacing(9) + theme.spacing(7)}; - .themeSwitch { - margin-left: theme.spacing(5); + @include theme.breakpoint("3xl") { + display: block; + position: absolute; + right: -#{theme.spacing(16)}; } } } } + + .desktopNavigation, + .desktopSupport, + .desktopDocs { + display: none; + @include theme.breakpoint("md") { + display: block; + } + } } diff --git a/apps/insights/src/components/Root/header.tsx b/apps/insights/src/components/Root/header.tsx index d83d733ccb..d1f54e6294 100644 --- a/apps/insights/src/components/Root/header.tsx +++ b/apps/insights/src/components/Root/header.tsx @@ -1,3 +1,5 @@ +"use client"; + import { Lifebuoy } from "@phosphor-icons/react/dist/ssr/Lifebuoy"; import { Button } from "@pythnetwork/component-library/Button"; import { Link } from "@pythnetwork/component-library/Link"; @@ -10,6 +12,7 @@ import { SearchButton } from "./search-button"; import { SupportDrawer } from "./support-drawer"; import { MainNavTabs } from "./tabs"; import { ThemeSwitch } from "./theme-switch"; +import { MobileMenu } from "../MobileMenu/mobile-menu"; export const Header = ({ className, ...props }: ComponentProps<"header">) => (
@@ -22,24 +25,31 @@ export const Header = ({ className, ...props }: ComponentProps<"header">) => (
Pyth Homepage
Insights
- +
+ +
- - - +
+ + + +
- +
+ +
+
diff --git a/apps/insights/src/components/Root/index.tsx b/apps/insights/src/components/Root/index.tsx index e3fb96107d..b2fd1e2a88 100644 --- a/apps/insights/src/components/Root/index.tsx +++ b/apps/insights/src/components/Root/index.tsx @@ -18,6 +18,7 @@ import { toHex } from "../../hex"; import { getPublishers } from "../../services/clickhouse"; import { Cluster, getData } from "../../services/pyth"; import { LivePricesProvider } from "../LivePrices"; +import { MobileNavigation } from "../MobileNavigation/mobile-navigation"; import { PriceFeedIcon } from "../PriceFeedIcon"; import { PublisherIcon } from "../PublisherIcon"; @@ -65,6 +66,7 @@ export const Root = async ({ children }: Props) => { {children}
+ diff --git a/apps/insights/src/components/Root/search-button.tsx b/apps/insights/src/components/Root/search-button.tsx index d099ba7431..0f421acb4f 100644 --- a/apps/insights/src/components/Root/search-button.tsx +++ b/apps/insights/src/components/Root/search-button.tsx @@ -31,7 +31,7 @@ const SearchText = () => { const SearchTextImpl = () => { // This component can only ever render in the client so we can safely ignore // this eslint rule. - // eslint-disable-next-line n/no-unsupported-features/node-builtins + const isMac = useMemo(() => navigator.userAgent.includes("Mac"), []); return isMac ? "⌘ K" : "Ctrl K"; }; diff --git a/apps/insights/src/components/Root/support-drawer.module.scss b/apps/insights/src/components/Root/support-drawer.module.scss index f6152c85d3..9ef4761f9a 100644 --- a/apps/insights/src/components/Root/support-drawer.module.scss +++ b/apps/insights/src/components/Root/support-drawer.module.scss @@ -34,12 +34,12 @@ grid-template-columns: max-content 1fr max-content; grid-template-rows: max-content max-content; text-align: left; - gap: theme.spacing(2) theme.spacing(4); - align-items: center; + gap: theme.spacing(1) theme.spacing(4); + align-items: start; width: 100%; .icon { - font-size: theme.spacing(8); + font-size: theme.spacing(6); color: theme.color("states", "data", "normal"); grid-row: span 2 / span 2; display: grid; @@ -47,17 +47,22 @@ } .header { - @include theme.text("sm", "medium"); + @include theme.h6; + line-height: 1.5; color: theme.color("heading"); } .description { - @include theme.text("xs", "normal"); + @include theme.text("sm", "normal"); + line-height: 1.5; color: theme.color("muted"); grid-column: 2; grid-row: 2; + word-wrap: break-word; + overflow: hidden; + max-width: 100%; } .caret { diff --git a/apps/insights/src/components/Stats/index.module.scss b/apps/insights/src/components/Stats/index.module.scss new file mode 100644 index 0000000000..8cecc3044f --- /dev/null +++ b/apps/insights/src/components/Stats/index.module.scss @@ -0,0 +1,60 @@ +@use "@pythnetwork/component-library/theme"; + +.statsContainer { + .statScrollWrapper { + width: 100dvw; // Extend the width beyond the root padding + margin-left: -#{theme.spacing(4)}; + margin-right: -#{theme.spacing(4)}; + white-space: nowrap; + overflow: auto hidden; + -webkit-overflow-scrolling: touch; + scroll-snap-type: x mandatory; + scroll-padding: theme.spacing(4); + left: 0; + right: 0; + + // Optional: Hide scrollbars + scrollbar-width: none; + + &::-webkit-scrollbar { + display: none; + } + + @include theme.breakpoint("md") { + width: 100%; + position: relative; + display: flex; + flex-flow: row nowrap; + overflow: visible; + margin: 0; + } + + .statsItemsContainer { + display: flex; + flex-flow: row nowrap; + width: max-content; + gap: theme.spacing(3); + padding: 0 theme.spacing(4); + + > * { + width: 280px; + scroll-snap-align: start; + } + + @include theme.breakpoint("md") { + gap: theme.spacing(6); + width: 100%; + padding: 0; + + > * { + display: flex; + width: 100%; + flex: 1 0 0; + min-width: 0; + max-width: 100%; + min-height: 0; + } + } + } + } +} diff --git a/apps/insights/src/components/Stats/index.tsx b/apps/insights/src/components/Stats/index.tsx new file mode 100644 index 0000000000..1a379e913e --- /dev/null +++ b/apps/insights/src/components/Stats/index.tsx @@ -0,0 +1,18 @@ +import clsx from "clsx"; +import type { ComponentProps } from "react"; + +import styles from "./index.module.scss"; + +type StatsProps = { + children: React.ReactNode; +} & ComponentProps<"div">; + +export const Stats = ({ children, ...props }: StatsProps) => { + return ( +
+
+
{children}
+
+
+ ); +}; diff --git a/apps/insights/src/components/StructuredList/index.module.scss b/apps/insights/src/components/StructuredList/index.module.scss new file mode 100644 index 0000000000..150d791f45 --- /dev/null +++ b/apps/insights/src/components/StructuredList/index.module.scss @@ -0,0 +1,27 @@ +@use "@pythnetwork/component-library/theme"; + +.structuredList { + display: flex; + flex-flow: column nowrap; + gap: theme.spacing(4); + + .structuredListItem { + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: theme.spacing(2); + align-items: center; + justify-content: space-between; + + // font-size: theme.font-size("sm"); + .structuredListItemLabel { + color: theme.color("muted"); + } + + .structuredListItemValue { + text-align: right; + place-items: end; + font-weight: theme.font-weight("medium"); + color: theme.color("heading"); + } + } +} diff --git a/apps/insights/src/components/StructuredList/index.tsx b/apps/insights/src/components/StructuredList/index.tsx new file mode 100644 index 0000000000..6ac09802b2 --- /dev/null +++ b/apps/insights/src/components/StructuredList/index.tsx @@ -0,0 +1,41 @@ +import clsx from "clsx"; +import type { ComponentProps } from "react"; + +import styles from "./index.module.scss"; + +type StructuredListProps = { + items: StructureListItemProps[]; +} & ComponentProps<"div">; + +type StructureListItemProps = { + label: React.ReactNode; + value: React.ReactNode; +} & ComponentProps<"div">; + +export const StructuredList = ({ items, ...props }: StructuredListProps) => { + return ( + items.length > 0 && ( +
+ {items.map((item, index) => ( + + ))} +
+ ) + ); +}; + +export const StructuredListItem = ({ + label, + value, + ...props +}: StructureListItemProps) => { + return ( +
+
{label}
+
{value}
+
+ ); +}; diff --git a/packages/component-library/src/Badge/index.module.scss b/packages/component-library/src/Badge/index.module.scss index 8ce2c49fe9..891a98dbfd 100644 --- a/packages/component-library/src/Badge/index.module.scss +++ b/packages/component-library/src/Badge/index.module.scss @@ -1,7 +1,9 @@ @use "../theme"; .badge { - display: inline flow-root; + display: inline-flex; + align-items: center; + justify-content: center; border-radius: theme.border-radius("3xl"); transition-property: color, background-color, border-color; transition-duration: 100ms; @@ -9,6 +11,7 @@ white-space: nowrap; border-width: 1px; border-style: solid; + flex-shrink: 1; &[data-size="xs"] { line-height: theme.spacing(4); diff --git a/packages/component-library/src/Button/index.module.scss b/packages/component-library/src/Button/index.module.scss index 6de1e6fc7a..57379baeaa 100644 --- a/packages/component-library/src/Button/index.module.scss +++ b/packages/component-library/src/Button/index.module.scss @@ -1,7 +1,7 @@ @use "../theme"; .button { - display: inline flow-root; + display: flex; cursor: pointer; white-space: nowrap; font-weight: theme.font-weight("medium"); @@ -12,6 +12,9 @@ text-decoration: none; outline-offset: 0; outline: theme.spacing(1) solid transparent; + text-align: center; + justify-content: center; + align-items: center; .iconWrapper { display: inline-grid; diff --git a/packages/component-library/src/Card/index.module.scss b/packages/component-library/src/Card/index.module.scss index 2f127ce2a6..dd79ee1e76 100644 --- a/packages/component-library/src/Card/index.module.scss +++ b/packages/component-library/src/Card/index.module.scss @@ -33,35 +33,43 @@ } .header { - display: flex; - padding: theme.spacing(3) theme.spacing(4); position: relative; - - .title { - color: theme.color("heading"); - display: inline-flex; + display: flex; + padding: theme.spacing(3); + gap: theme.spacing(3); + flex-direction: column; + justify-content: flex-start; + + @include theme.breakpoint("lg") { + padding: 0 theme.spacing(2); + height: theme.spacing(12); + justify-content: space-between; flex-flow: row nowrap; - gap: theme.spacing(3); align-items: center; + } - @include theme.text("lg", "medium"); - - .icon { - font-size: theme.spacing(6); - height: theme.spacing(6); - color: theme.color("button", "primary", "background", "normal"); + .toolbar { + &:empty { + display: none; } + gap: theme.spacing(2); + display: flex; } - .toolbar { + .action { + align-self: center; + grid-area: action; + // display: grid; + // place-items: center; + // height: 100%; + // padding: 0 theme.spacing(2); position: absolute; - right: theme.spacing(3); - top: 0; - bottom: theme.spacing(0); - display: flex; - flex-flow: row nowrap; - gap: theme.spacing(2); - align-items: center; + top: theme.spacing(1.5); + right: theme.spacing(1.5); + @include theme.breakpoint("md") { + position: static; + padding: 0; + } } } diff --git a/packages/component-library/src/Card/index.tsx b/packages/component-library/src/Card/index.tsx index de33b1c4cd..2d1b646552 100644 --- a/packages/component-library/src/Card/index.tsx +++ b/packages/component-library/src/Card/index.tsx @@ -20,6 +20,7 @@ type OwnProps = { icon?: ReactNode | undefined; title?: ReactNode | undefined; toolbar?: ReactNode | ReactNode[] | undefined; + action?: ReactNode | undefined; footer?: ReactNode | undefined; nonInteractive?: boolean | undefined; }; @@ -59,6 +60,7 @@ const cardProps = ({ title, toolbar, footer, + action, ...props }: Props) => ({ ...props, @@ -69,11 +71,9 @@ const cardProps = ({
{(Boolean(icon) || Boolean(title) || Boolean(toolbar)) && (
-

- {icon &&
{icon}
} - {title} -

-
{toolbar}
+
{title}
+ {toolbar &&
{toolbar}
} + {action &&
{action}
}
)} {children} diff --git a/packages/component-library/src/Drawer/index.module.scss b/packages/component-library/src/Drawer/index.module.scss index 3840e4b28e..039a26e6d5 100644 --- a/packages/component-library/src/Drawer/index.module.scss +++ b/packages/component-library/src/Drawer/index.module.scss @@ -7,31 +7,46 @@ z-index: 1; .drawer { - position: fixed; - top: theme.spacing(4); - bottom: theme.spacing(4); - right: theme.spacing(4); - width: 60%; - max-width: theme.spacing(160); - outline: none; + width: 100%; + border-radius: 0; + border: none; + padding-bottom: 0; background: theme.color("background", "primary"); - border: 1px solid theme.color("border"); - border-radius: theme.border-radius("3xl"); - display: flex; - flex-flow: column nowrap; - overflow-y: hidden; - padding-bottom: theme.border-radius("3xl"); + + @include theme.breakpoint("sm") { + position: fixed; + top: theme.spacing(4); + bottom: theme.spacing(4); + right: theme.spacing(4); + width: 80%; + max-width: theme.spacing(160); + outline: none; + border: 1px solid theme.color("background", "secondary"); + border-radius: theme.border-radius("3xl"); + display: flex; + flex-flow: column nowrap; + overflow-y: hidden; + } + + @include theme.breakpoint("lg") { + width: 60%; + } .heading { - padding: theme.spacing(4); - padding-left: theme.spacing(6); + padding: theme.spacing(3); + padding-left: theme.spacing(4); display: flex; flex-flow: row nowrap; justify-content: space-between; align-items: center; color: theme.color("heading"); flex: none; - border-bottom: 1px solid theme.color("border"); + border-bottom: 1px solid theme.color("background", "secondary"); + + @include theme.breakpoint("sm") { + padding: theme.spacing(4); + padding-left: theme.spacing(6); + } .title { @include theme.h4; @@ -51,8 +66,12 @@ .body { flex: 1; - overflow-y: auto; - padding: theme.spacing(6); + overflow: hidden auto; + padding: theme.spacing(4); + + @include theme.breakpoint("sm") { + padding: theme.spacing(6); + } } &[data-fill] { diff --git a/packages/component-library/src/MainNavTabs/index.module.scss b/packages/component-library/src/MainNavTabs/index.module.scss index 0d6e7573ef..c82cc45c65 100644 --- a/packages/component-library/src/MainNavTabs/index.module.scss +++ b/packages/component-library/src/MainNavTabs/index.module.scss @@ -2,12 +2,18 @@ .mainNavTabs { gap: theme.spacing(2); + z-index: 1; @include theme.row; .tab { + width: 100%; position: relative; outline: none; + z-index: 1; + @include theme.breakpoint("md") { + width: auto; + } .bubble { position: absolute; @@ -39,21 +45,11 @@ pointer-events: auto; &[data-hovered] .bubble { - background-color: theme.color( - "button", - "solid", - "background", - "hover" - ); + background-color: theme.color("button", "solid", "background", "hover"); } &[data-pressed] .bubble { - background-color: theme.color( - "button", - "solid", - "background", - "active" - ); + background-color: theme.color("button", "solid", "background", "active"); } } } diff --git a/packages/component-library/src/Paginator/index.module.scss b/packages/component-library/src/Paginator/index.module.scss index 5679663437..cd49af2658 100644 --- a/packages/component-library/src/Paginator/index.module.scss +++ b/packages/component-library/src/Paginator/index.module.scss @@ -3,14 +3,22 @@ .paginator { display: flex; flex-flow: row nowrap; - justify-content: space-between; + justify-content: center; + + @include theme.breakpoint("lg") { + justify-content: space-between; + } .pageSizeSelect { - display: flex; + display: none; flex-flow: row nowrap; align-items: center; gap: theme.spacing(1); + @include theme.breakpoint("lg") { + display: flex; + } + .loadingIndicator { width: theme.spacing(4); height: theme.spacing(4); diff --git a/packages/component-library/src/SingleToggleGroup/index.module.scss b/packages/component-library/src/SingleToggleGroup/index.module.scss index 5bc1f0d035..64a0400d7d 100644 --- a/packages/component-library/src/SingleToggleGroup/index.module.scss +++ b/packages/component-library/src/SingleToggleGroup/index.module.scss @@ -1,7 +1,7 @@ @use "../theme"; .singleToggleGroup { - gap: theme.spacing(2); + gap: theme.spacing(1); @include theme.row; diff --git a/packages/component-library/src/Table/index.module.scss b/packages/component-library/src/Table/index.module.scss index 6e96da9c1f..0b2d17abd8 100644 --- a/packages/component-library/src/Table/index.module.scss +++ b/packages/component-library/src/Table/index.module.scss @@ -1,9 +1,14 @@ @use "../theme"; .tableContainer { + display: none; background-color: theme.color("background", "primary"); position: relative; + @include theme.breakpoint("md") { + display: block; + } + .loaderWrapper { position: absolute; top: theme.spacing(10); @@ -83,7 +88,7 @@ .cell { position: relative; - border-bottom: 1px solid theme.color("border"); + border-bottom: 1px solid theme.color("background", "secondary"); font-weight: theme.font-weight("medium"); .divider { @@ -164,21 +169,11 @@ } &[data-hovered] .cell { - background-color: theme.color( - "button", - "outline", - "background", - "hover" - ); + background-color: theme.color("button", "outline", "background", "hover"); } &[data-pressed] .cell { - background-color: theme.color( - "button", - "outline", - "background", - "active" - ); + background-color: theme.color("button", "outline", "background", "active"); } } } diff --git a/packages/component-library/src/theme.scss b/packages/component-library/src/theme.scss index 0cfb0b6f9b..1f12d81998 100644 --- a/packages/component-library/src/theme.scss +++ b/packages/component-library/src/theme.scss @@ -80,6 +80,50 @@ $border-radius: ( @return map-get-strict($border-radius, $radius); } +$breakpoints: ( + "xs": 480px, + "sm": 720px, + "md": 960px, + "lg": 1024px, + "xl": 1280px, + "2xl": 1536px, + "3xl": 1720px, +); + +@function breakpoint-old($breakpoint) { + @return map-get-strict($breakpoints, $breakpoint); +} + +@mixin breakpoint($point) { + @media (min-width: map-get-strict($breakpoints, $point)) { + @content; + } +} + +@mixin mobile() { + @media screen and (max-width: breakpoint-old("md")) { + @content; + + background: cyan; + } +} + +@mixin tablet() { + @media screen and (min-width: breakpoint-old("md")) { + @content; + + background: orange; + } +} + +@mixin desktop() { + @media screen and (min-width: breakpoint-old("3xl")) { + @content; + + background: pink; + } +} + $color-pallette: ( "black": #000, "white": #fff, @@ -433,7 +477,7 @@ $color: ( "highlight": light-dark(pallette-color("violet", 600), pallette-color("violet", 500)), "muted": - light-dark(pallette-color("stone", 700), pallette-color("steel", 300)), + light-dark(pallette-color("stone", 500), pallette-color("steel", 400)), "border": light-dark(pallette-color("stone", 300), pallette-color("steel", 600)), "selection": ( @@ -724,8 +768,12 @@ $max-width: 96rem; @mixin max-width { margin: 0 auto; max-width: $max-width; - padding: 0 spacing(6); + padding: 0 spacing(4); box-sizing: content-box; + + @include breakpoint("xl") { + padding: 0 spacing(6); + } } @mixin row { @@ -784,6 +832,24 @@ $elevations: ( margin: 0; } +@mixin h5 { + font-size: font-size("lg"); + font-style: normal; + font-weight: font-weight("medium"); + line-height: 125%; + letter-spacing: letter-spacing("tight"); + margin: 0; +} + +@mixin h6 { + font-size: font-size("base"); + font-style: normal; + font-weight: font-weight("medium"); + line-height: 125%; + letter-spacing: letter-spacing("tight"); + margin: 0; +} + @mixin text($size: "base", $weight: "normal") { font-size: font-size($size); font-weight: font-weight($weight);