diff --git a/app/(layout)/applications/[owner_project]/page.tsx b/app/(layout)/applications/[owner_project]/page.tsx
index 2a8debc1..eb89d740 100644
--- a/app/(layout)/applications/[owner_project]/page.tsx
+++ b/app/(layout)/applications/[owner_project]/page.tsx
@@ -8,8 +8,8 @@ import { useMaster } from "@/contexts/MasterContext";
import { StackedDataBar } from "../_components/GTPChart";
import { ChartScaleProvider } from "../_contexts/ChartScaleContext";
import ChartScaleControls from "../_components/ChartScaleControls";
-import { ApplicationDisplayName, ApplicationIcon, Category, Chains, Links, MetricTooltip } from "../_components/Components";
-import { memo, useEffect, useMemo, useRef, useState } from "react";
+import { ApplicationDisplayName, ApplicationIcon, Category, Chains, formatNumber, Links, MetricTooltip } from "../_components/Components";
+import { memo, useCallback, useEffect, useMemo, useRef, useState } from "react";
import { useLocalStorage } from "usehooks-ts";
import HorizontalScrollContainer from "@/components/HorizontalScrollContainer";
import { GridTableAddressCell, GridTableHeader, GridTableHeaderCell, GridTableRow } from "@/components/layout/GridTable";
@@ -20,6 +20,11 @@ import VerticalVirtuosoScrollContainer from "@/components/VerticalVirtuosoScroll
import Link from "next/link";
import { SortProvider, useSort } from "../_contexts/SortContext";
import { useUIContext } from "@/contexts/UIContext";
+import { GTPIconName } from "@/icons/gtp-icon-names";
+import { Virtuoso } from "react-virtuoso";
+import { useRouter } from "next/navigation";
+import { AggregatedDataRow, useApplicationsData } from "../_contexts/ApplicationsDataContext";
+import useDragScroll from "@/hooks/useDragScroll";
type Props = {
params: { owner_project: string };
@@ -70,7 +75,7 @@ export default function Page({ params: { owner_project } }: Props) {
-
+ {/*
*/}
Similar Applications
@@ -80,8 +85,10 @@ export default function Page({ params: { owner_project } }: Props) {
-
+ {/*
*/}
+
+
>
);
}
@@ -251,32 +258,6 @@ const MetricChainBreakdownBar = ({ metric }: { metric: string }) => {
return [...acc, prev + v];
}, [] as number[]);
- function formatNumber(number: number, decimals?: number): string {
- if (number === 0) {
- return "0";
- } else if (Math.abs(number) >= 1e9) {
- if (Math.abs(number) >= 1e12) {
- return (number / 1e12).toFixed(2) + "T";
- } else if (Math.abs(number) >= 1e9) {
- return (number / 1e9).toFixed(2) + "B";
- }
- } else if (Math.abs(number) >= 1e6) {
- return (number / 1e6).toFixed(2) + "M";
- } else if (Math.abs(number) >= 1e3) {
- const rounded = (number / 1e3).toFixed(2);
- return `${rounded}${Math.abs(number) >= 10000 ? "k" : "k"}`;
- } else if (Math.abs(number) >= 100) {
- return number.toFixed(decimals ? decimals : 2);
- } else if (Math.abs(number) >= 10) {
- return number.toFixed(decimals ? decimals : 2);
- } else {
- return number.toFixed(decimals ? decimals : 2);
- }
-
- // Default return if none of the conditions are met
- return "";
- }
-
console.log("cumulativePercentages", cumulativePercentages);
if (!metricData) {
@@ -291,7 +272,7 @@ const MetricChainBreakdownBar = ({ metric }: { metric: string }) => {
-
{prefix}{formatNumber(values.reduce((acc, v) => acc + v, 0))}
+
{prefix}{formatNumber(values.reduce((acc, v) => acc + v, 0), {defaultDecimals: 2, thresholdDecimals: {base:5}})}
{ownerProjectToProjectData[owner_project] && ownerProjectToProjectData[owner_project].display_name || ""}
@@ -379,6 +360,7 @@ const ContractsTable = () => {
const { metricsDef, selectedMetrics, setSelectedMetrics, selectedMetricKeys, } = useMetrics();
const [showUsd, setShowUsd] = useLocalStorage("showUsd", true);
const {sort, setSort} = useSort();
+ const [showMore, setShowMore] = useState(false);
const metricKey = useMemo(() => {
let key = selectedMetrics[0];
@@ -390,7 +372,7 @@ const ContractsTable = () => {
// Memoize gridColumns to prevent recalculations
const gridColumns = useMemo(() =>
- `26px 280px 95px minmax(95px,800px) ${selectedMetricKeys.map(() => `237px`).join(" ")} 20px`,
+ `26px 280px 95px minmax(135px,800px) ${selectedMetricKeys.map(() => `237px`).join(" ")} 20px`,
[selectedMetricKeys]
);
@@ -445,7 +427,7 @@ const ContractsTable = () => {
})}
- */}
+
+
(
+
+
+
+ )}
+ useWindowScroll
+ increaseViewportBy={{top:0, bottom: 400}}
+ overscan={50}
+ />
+
+
+
setShowMore(!showMore)}>
+ {showMore ? "Show less" : "Show more"}
+
-
)
}
@@ -499,13 +498,17 @@ const ContractsTableRow = memo(({ contract }: { contract: ContractDict}) => {
const { owner_project } = useApplicationDetailsData();
const { ownerProjectToProjectData } = useProjectsMetadata();
const { metricsDef, selectedMetrics, selectedMetricKeys, } = useMetrics();
+ const { AllChainsByKeys, data: masterData } = useMaster();
// Memoize gridColumns to prevent recalculations
const gridColumns = useMemo(() =>
- `26px 280px 95px minmax(95px,800px) ${selectedMetricKeys.map(() => `237px`).join(" ")} 20px`,
+ `26px 280px 95px minmax(135px,800px) ${selectedMetricKeys.map(() => `237px`).join(" ")} 20px`,
[selectedMetricKeys]
);
+ if(!masterData)
+ return null;
+
return (
{
{/*
*/}
{contract.name || (
)}
-
+ {/*
*/}
{/*
@@ -532,8 +538,8 @@ const ContractsTableRow = memo(({ contract }: { contract: ContractDict}) => {
-
- {contract.sub_category_key}
+
+ {masterData.blockspace_categories.sub_categories[contract.sub_category_key] ? masterData.blockspace_categories.sub_categories[contract.sub_category_key] : contract.sub_category_key}
{selectedMetrics.map((key, index) => (
{
)
});
-ContractsTableRow.displayName = 'ApplicationTableRow';
\ No newline at end of file
+ContractsTableRow.displayName = 'ApplicationTableRow';
+
+const SimilarApplications = ({ owner_project }: { owner_project: string }) => {
+ const {ownerProjectToProjectData} = useProjectsMetadata();
+ const { applicationDataAggregated, isLoading } = useApplicationsData();
+ const { selectedMetrics } = useMetrics();
+ const { metricsDef } = useMetrics();
+ const { sort } = useSort();
+
+ const [medianMetricKey, setMedianMetricKey] = useState(selectedMetrics[0]);
+
+ useEffect(() => {
+ if(Object.keys(metricsDef).includes(sort.metric)){
+ let key = sort.metric;
+ if (sort.metric === "gas_fees")
+ key = "gas_fees_eth";
+
+ setMedianMetricKey(key);
+ }
+
+ }, [metricsDef, sort.metric]);
+
+ const { topGainers, topLosers } = useMemo(() => {
+
+ if(!ownerProjectToProjectData || !ownerProjectToProjectData[owner_project] || !applicationDataAggregated || !applicationDataAggregated.length)
+ return { topGainers: [], topLosers: [] };
+ // let medianMetricKey = Object.keys(metricsDef).includes(sort.metric) ? sort.metric : "gas_fees";
+
+ const medianMetricValues = applicationDataAggregated.map((application) => application[medianMetricKey])
+ .sort((a, b) => a - b);
+
+ const medianValue = medianMetricValues[Math.floor(medianMetricValues.length / 2)];
+
+ // console.log("medianMetricKey", medianMetricKey);
+ // console.log("medianValue", medianValue);
+ // console.log("applicationDataAggregated", applicationDataAggregated);
+
+ // filter out applications with < median value of selected metric and with previous value of 0
+ const filteredApplications = applicationDataAggregated
+ .filter((application) => application[medianMetricKey] > medianValue && application["prev_" + medianMetricKey] > 0 && ownerProjectToProjectData[application.owner_project].main_category === ownerProjectToProjectData[owner_project].main_category)
+ // console.log("filteredApplications", filteredApplications);
+
+ // top 3 applications with highest change_pct
+ return {
+ topGainers: [...filteredApplications]
+ .sort((a, b) => b[medianMetricKey + "_change_pct"] - a[medianMetricKey + "_change_pct"])
+ .slice(0, 3),
+ topLosers: [...filteredApplications]
+ .sort((a, b) => a[medianMetricKey + "_change_pct"] - b[medianMetricKey + "_change_pct"])
+ .slice(0, 3),
+ }
+ }, [applicationDataAggregated, medianMetricKey, ownerProjectToProjectData, owner_project]);
+
+ return (
+ <>
+
+
+ {topGainers.map((application, index) => (
+
+ ))}
+ {topLosers.map((application, index) => (
+
+ ))}
+ {isLoading && new Array(6).fill(0).map((_, index) => (
+
+ ))}
+
+
+
+
), ...topLosers.map((application) => )]} />
+
+ >
+ )
+}
+
+
+const CardSwiper = ({ cards }: { cards: React.ReactNode[] }) => {
+ // const containerRef = useRef
(null);
+ const [activeIndex, setActiveIndex] = useState(0);
+ const [isFirst, setIsFirst] = useState(true);
+ const [isLast, setIsLast] = useState(false);
+ const [leftIndex, setLeftIndex] = useState(0);
+ const [rightIndex, setRightIndex] = useState(2);
+
+ const { containerRef, showLeftGradient, showRightGradient } =
+ useDragScroll("horizontal", 0.96, { snap: true, snapThreshold: 0.2 });
+
+ const onScroll = () => {
+ if (containerRef.current) {
+ const containerRect = containerRef.current.getBoundingClientRect();
+ const containerCenter = containerRect.left + containerRect.width / 2;
+ const children = Array.from(containerRef.current.children);
+ let closestIndex = 0;
+ let closestDistance = Infinity;
+ children.forEach((child, index) => {
+ const rect = child.getBoundingClientRect();
+ const childCenter = rect.left + rect.width / 2;
+ const distance = Math.abs(childCenter - containerCenter);
+ if (distance < closestDistance) {
+ closestDistance = distance;
+ closestIndex = index;
+ }
+ });
+ setActiveIndex(closestIndex);
+ setLeftIndex(Math.max(0, closestIndex - 1));
+ setRightIndex(Math.min(children.length - 1, closestIndex + 1));
+ setIsFirst(closestIndex === 0);
+ setIsLast(closestIndex === children.length - 1);
+ }
+ };
+
+ useEffect(() => {
+ const container = containerRef.current;
+ if (!container) return;
+ container.addEventListener("scroll", onScroll);
+ // Run once on mount to set the active index correctly.
+ onScroll();
+ return () => container.removeEventListener("scroll", onScroll);
+ }, []);
+
+ return (
+
+ {cards.map((card, index) => {
+ return (
+
+ {card}
+
+ )
+ })}
+
+ );
+};
+
+
+
+const ApplicationCard = memo(({ application, className, width }: { application?: AggregatedDataRow, className?: string, width?: number }) => {
+ const { AllChainsByKeys } = useMaster();
+ const { ownerProjectToProjectData } = useProjectsMetadata();
+ const { selectedChains, setSelectedChains, } = useApplicationsData();
+ const { selectedMetrics, metricsDef } = useMetrics();
+ const [showUsd, setShowUsd] = useLocalStorage("showUsd", true);
+ const router = useRouter();
+
+ const numContractsString = useCallback((application: AggregatedDataRow) => {
+ return application.num_contracts.toLocaleString("en-GB");
+ }, []);
+
+
+ const metricKey = useMemo(() => {
+ let key = selectedMetrics[0];
+ if (selectedMetrics[0] === "gas_fees")
+ key = showUsd ? "gas_fees_usd" : "gas_fees_eth";
+
+ return key;
+ }, [selectedMetrics, showUsd]);
+
+ const rank = useMemo(() => {
+ if (!application) return null;
+
+ return application[`rank_${metricKey}`];
+
+ }, [application, metricKey]);
+
+ const value = useMemo(() => {
+ if (!application) return null;
+
+ return application[metricKey];
+ }, [application, metricKey]);
+
+ const prefix = useMemo(() => {
+ const def = metricsDef[selectedMetrics[0]].units;
+
+ if (Object.keys(def).includes("usd")) {
+ return showUsd ? def.usd.prefix : def.eth.prefix;
+ } else {
+ return Object.values(def)[0].prefix;
+ }
+ }, [metricsDef, selectedMetrics, showUsd]);
+
+ if (!application) {
+ return (
+
+
+ )
+ }
+
+ return (
+ {
+ // window.location.href = `/applications/${application.owner_project}`;
+ router.push(`/applications/${application.owner_project}`);
+ }}
+ >
+
+
+
+
{numContractsString(application)}
+
contracts
+
+
+
+
+
+
+ {/* {JSON.stringify( application)} */}
+ {application.origin_keys.map((chain, index) => (
+
{
+ if (selectedChains.includes(chain)) {
+ setSelectedChains(selectedChains.filter((c) => c !== chain));
+ } else {
+ setSelectedChains([...selectedChains, chain]);
+ }
+ }}
+ >
+ {AllChainsByKeys[chain] && (
+
+ )}
+
+ ))}
+
+
+
+
+ {prefix}
+ {value?.toLocaleString("en-GB")}
+
+
+ {application[`${metricKey}_change_pct`] !== Infinity ? (
+
+ {/* {application[`${metricKey}_change_pct`] < 0 ? '-' : '+'}{formatNumber(Math.abs(application[`${metricKey}_change_pct`]), {defaultDecimals: 1, thresholdDecimals: {base:0}})}% */}
+ {application[`${metricKey}_change_pct`] < 0 ? '-' : '+'}{Math.abs(application[`${metricKey}_change_pct`]).toLocaleString("en-GB",{maximumFractionDigits:0})}%
+
+ ) :
}
+
+
+
+
+
+
+
+ {ownerProjectToProjectData[application.owner_project] ? (
+
+ ) : (
+
+ )}
+
+
+
+
+
+
{ownerProjectToProjectData[application.owner_project] && ownerProjectToProjectData[application.owner_project].main_category}
+ {/*
+ {getLinks(application).map((item, index) => (
+
+ {item.link &&
+
+ }
+
+ ))}
+
*/}
+
+
+
+ )
+});
+
+ApplicationCard.displayName = 'ApplicationCard';
\ No newline at end of file
diff --git a/app/(layout)/applications/_components/Components.tsx b/app/(layout)/applications/_components/Components.tsx
index 7dbb549d..cc041159 100644
--- a/app/(layout)/applications/_components/Components.tsx
+++ b/app/(layout)/applications/_components/Components.tsx
@@ -114,9 +114,9 @@ export const PageTitleAndDescriptionAndControls = () => {
*/}
-
+
-
+
Applications
{/* {scrollY} {`${Math.min(0, -88 + scrollY)}px`} */}
@@ -136,7 +136,7 @@ export const PageTitleAndDescriptionAndControls = () => {
@@ -146,7 +146,12 @@ export const PageTitleAndDescriptionAndControls = () => {
{/* Relay is a cross-chain payment system that enables instant, low-cost bridging and transaction execution by connecting users with relayers who act on their behalf for a small fee. It aims to minimize gas costs and execution latency, making it suitable for applications like payments, bridging, NFT minting, and gas abstraction. I can add one more sentence to that and its still legible. And one more maybe so that we reach 450 characters. Let’s see. */}
+
+
{/* */}
@@ -215,7 +220,7 @@ export const MultipleSelectTopRowChild = ({ handleNext, handlePrev, selected, se
<>
{
+export const ProjectDetailsLinks = memo(({ owner_project, mobile }: { owner_project: string, mobile?: boolean }) => {
"use client";
const { ownerProjectToProjectData } = useProjectsMetadata();
const linkPrefixes = ["https://x.com/", "https://github.com/", "", ""];
const icons = ["ri:twitter-x-fill", "ri:github-fill", "feather:monitor", "ri:discord-fill"];
const keys = ["twitter", "main_github", "website", "discord"];
+ if(mobile) {
+ return (
+
+ {ownerProjectToProjectData[owner_project] && keys.filter(
+ (key) => ownerProjectToProjectData[owner_project][key]
+ ).map((key, index) => (
+
+ {
+ // key === "website" ?
+ // (
+ // <>
+ // {ownerProjectToProjectData[owner_project] && (
+ //
+ // )}
+ //
{ownerProjectToProjectData[owner_project].display_name}
+ //
+ //
+ //
+ // >
+ // )
+ // : (
+
+ // )
+ }
+
+ ))}
+
+ );
+ }
+
return (
{ownerProjectToProjectData[owner_project] && keys.filter(
@@ -389,6 +432,8 @@ export const Links = memo(({ owner_project, showUrl}: { owner_project: string, s
const keys = ["website", "twitter", "main_github"];
const [currentHoverKey, setCurrentHover] = useState("website");
+ if(!ownerProjectToProjectData[owner_project]) return null;
+
if(showUrl) {
return (
@@ -440,12 +485,13 @@ export const Chains = ({ origin_keys }: { origin_keys: string[] }) => {
const { selectedChains, setSelectedChains } = useApplicationsData();
return (
-
+
{origin_keys.map((chain, index) => (
{
+ className={`group-hover/chains:opacity-50 hover:!opacity-100 cursor-pointer p-[2.5px] ${selectedChains.includes(chain) || selectedChains.length === 0 ? '' : '!text-[#5A6462]'}`} style={{ color: AllChainsByKeys[chain] ? AllChainsByKeys[chain].colors["dark"][0] : '' }}
+ onClick={(e) => {
+ e.stopPropagation();
if (selectedChains.includes(chain)) {
setSelectedChains(selectedChains.filter((c) => c !== chain));
} else {
@@ -507,4 +553,51 @@ export const Category = ({ category }: { category: string }) => {
)}
);
+}
+
+interface ThresholdConfig {
+ value: number;
+ suffix: string;
+ decimals?: number;
+}
+
+interface FormatNumberOptions {
+ defaultDecimals?: number;
+ thresholdDecimals?: {
+ [key: string]: number; // 'T', 'B', 'M', 'k', or 'base' for numbers < 1000
+ };
+}
+
+export function formatNumber(
+ number: number,
+ options: FormatNumberOptions = {}
+): string {
+ // Handle special cases
+ if (!Number.isFinite(number)) return 'N/A';
+ if (number === 0) return '0';
+
+ const defaultDecimals = options.defaultDecimals ?? 2;
+
+ // Define formatting thresholds
+ const thresholds: ThresholdConfig[] = [
+ { value: 1e12, suffix: 'T' },
+ { value: 1e9, suffix: 'B' },
+ { value: 1e6, suffix: 'M' },
+ { value: 1e3, suffix: 'k' }
+ ];
+
+ const absNumber = Math.abs(number);
+
+ // Find the appropriate threshold
+ const threshold = thresholds.find(t => absNumber >= t.value);
+
+ if (threshold) {
+ const scaledNumber = number / threshold.value;
+ const decimals = options.thresholdDecimals?.[threshold.suffix] ?? defaultDecimals;
+ return scaledNumber.toFixed(decimals) + threshold.suffix;
+ }
+
+ // For numbers less than 1000
+ const baseDecimals = options.thresholdDecimals?.['base'] ?? defaultDecimals;
+ return number.toFixed(baseDecimals);
}
\ No newline at end of file
diff --git a/app/(layout)/applications/_components/Search.tsx b/app/(layout)/applications/_components/Search.tsx
index 865f3372..d1d50628 100644
--- a/app/(layout)/applications/_components/Search.tsx
+++ b/app/(layout)/applications/_components/Search.tsx
@@ -333,7 +333,7 @@ export default function Search() {
}}
/>
setIsOpen(true)}
>
diff --git a/app/(layout)/applications/page.tsx b/app/(layout)/applications/page.tsx
index c8b5cdbc..8bd3f07c 100644
--- a/app/(layout)/applications/page.tsx
+++ b/app/(layout)/applications/page.tsx
@@ -18,7 +18,7 @@ import { useLocalStorage } from "usehooks-ts";
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/layout/Tooltip";
import VerticalVirtuosoScrollContainer from "@/components/VerticalVirtuosoScrollContainer";
import { Virtuoso } from "react-virtuoso";
-import { ApplicationDisplayName, ApplicationIcon, Category, Chains, Links, MetricTooltip } from "./_components/Components";
+import { ApplicationDisplayName, ApplicationIcon, Category, Chains, formatNumber, Links, MetricTooltip } from "./_components/Components";
import { useProjectsMetadata } from "./_contexts/ProjectsMetadataContext";
import { useSort } from "./_contexts/SortContext";
import { ApplicationsURLs } from "@/lib/urls";
@@ -27,6 +27,7 @@ import useDragScroll from "@/hooks/useDragScroll";
import { useRouter } from "next/navigation";
import Image from "next/image";
import { MetricInfo } from "@/types/api/MasterResponse";
+import { useTimespan } from "./_contexts/TimespanContext";
// Preload data for the overview page
@@ -39,6 +40,7 @@ export default function Page() {
const { selectedMetrics } = useMetrics();
const { metricsDef } = useMetrics();
const { sort } = useSort();
+ const { selectedTimespan } = useTimespan();
const [medianMetricKey, setMedianMetricKey] = useState(selectedMetrics[0]);
@@ -61,14 +63,14 @@ export default function Page() {
const medianValue = medianMetricValues[Math.floor(medianMetricValues.length / 2)];
- console.log("medianMetricKey", medianMetricKey);
- console.log("medianValue", medianValue);
- console.log("applicationDataAggregated", applicationDataAggregated);
+ // console.log("medianMetricKey", medianMetricKey);
+ // console.log("medianValue", medianValue);
+ // console.log("applicationDataAggregated", applicationDataAggregated);
// filter out applications with < median value of selected metric and with previous value of 0
const filteredApplications = applicationDataAggregated
.filter((application) => application[medianMetricKey] > medianValue && application["prev_" + medianMetricKey] > 0);
- console.log("filteredApplications", filteredApplications);
+ // console.log("filteredApplications", filteredApplications);
// top 3 applications with highest change_pct
return {
@@ -85,7 +87,7 @@ export default function Page() {
<>
{/*
*/}
-
+
Top Gainers and Losers by {metricsDef[selectedMetrics[0]].name}
@@ -93,7 +95,7 @@ export default function Page() {
-
+
{topGainers.map((application, index) => (
))}
@@ -106,7 +108,7 @@ export default function Page() {
{/*
*/}
-
+
), ...topLosers.map((application) => )]} />
{/* */}
@@ -215,125 +217,6 @@ const CardSwiper = ({ cards }: { cards: React.ReactNode[] }) => {
);
};
-const ApplicationCardSwiper = () => {
- const { applicationDataAggregated } = useApplicationsData();
- const [cardWidth, setCardWidth] = useState(340);
- const containerRef = useRef
(null);
- const [scrollLeft, setScrollLeft] = useState(0);
- const [containerWidth, setContainerWidth] = useState(0);
-
- // Update container width on resize
- useEffect(() => {
- const handleResize = () => {
- if (containerRef.current) {
- setContainerWidth(containerRef.current.offsetWidth);
- }
- };
-
- handleResize();
- window.addEventListener('resize', handleResize);
- return () => window.removeEventListener('resize', handleResize);
- }, []);
-
- // Calculate visible applications
- const visibleApplications = useMemo(() => {
- return [...applicationDataAggregated.slice(0, 3), ...applicationDataAggregated.slice(-3)];
- }, [applicationDataAggregated]);
-
- // Dragging state
- const [dragging, setDragging] = useState(false);
- const [dragStartX, setDragStartX] = useState(0);
-
- // Handle mouse down event
- const handleMouseDown = (e: React.MouseEvent) => {
- setDragging(true);
- setDragStartX(e.screenX);
- };
-
- // Handle mouse up event
- const handleMouseUp = () => {
- setDragging(false);
- };
-
- // Handle mouse move event
- const handleMouseMove = (e: MouseEvent) => {
- if (dragging && containerRef.current) {
- const dx = e.screenX - dragStartX;
- const newScrollLeft = scrollLeft - dx;
- const maxScroll = containerRef.current.scrollWidth - containerRef.current.clientWidth;
-
- // Ensure scrollLeft stays within bounds
- const boundedScrollLeft = Math.max(0, Math.min(maxScroll, newScrollLeft));
-
- // Update scroll position
- containerRef.current.scrollLeft = boundedScrollLeft;
- setScrollLeft(boundedScrollLeft);
- setDragStartX(e.screenX);
- }
- };
-
- useEffect(() => {
- // Update container width on resize
- const updateContainerWidth = () => {
- setCardWidth(window.innerWidth - 40);
- };
-
- updateContainerWidth();
- window.addEventListener('resize', updateContainerWidth);
-
- // Add global event listeners
- document.addEventListener('mouseup', handleMouseUp);
- document.addEventListener('mousemove', handleMouseMove);
-
- // Cleanup
- return () => {
- window.removeEventListener('resize', updateContainerWidth);
- document.removeEventListener('mouseup', handleMouseUp);
- document.removeEventListener('mousemove', handleMouseMove);
- };
- }, [dragging, dragStartX, scrollLeft]); // Add dependencies
-
- useEffect(() => {
- setCardWidth(window.innerWidth - 40);
- }, []);
-
-
- return (
- setScrollLeft(e.currentTarget.scrollLeft)}
- onMouseDown={handleMouseDown}
- onScroll={(e) => setScrollLeft(e.currentTarget.scrollLeft)}
-
-
- >
- {visibleApplications.map((application, index) => {
- // Calculate scaling and translation to give a carousel effect
- const distance = (index * cardWidth) - scrollLeft;
- const scaleX = 0.85 + 0.15 * (1 - Math.abs(distance) / containerWidth);
- const scaleY = 0.85 + 0.15 * (1 - Math.abs(distance) / containerWidth);
-
- return (
-
- );
- })}
-
- );
-};
const ApplicationCard = memo(({ application, className, width }: { application?: AggregatedDataRow, className?: string, width?: number }) => {
@@ -397,23 +280,18 @@ const ApplicationCard = memo(({ application, className, width }: { application?:
}}
>
-
-
+
+
{numContractsString(application)}
contracts
-
+
Rank
{rank}
- {application[`${metricKey}_change_pct`] !== Infinity ? (
-
- {application[`${metricKey}_change_pct`] < 0 ? '-' : '+'}{Math.abs(application[`${metricKey}_change_pct`]).toFixed(0)}%
-
- ) :
}
-
-
+
+
{/* {JSON.stringify( application)} */}
{application.origin_keys.map((chain, index) => (
@@ -443,10 +321,20 @@ const ApplicationCard = memo(({ application, className, width }: { application?:
))}
-
-
- {prefix}
- {value?.toLocaleString("en-GB")}
+
+
+
+ {prefix}
+ {value?.toLocaleString("en-GB")}
+
+
+ {application[`${metricKey}_change_pct`] !== Infinity ? (
+
+ {/* {application[`${metricKey}_change_pct`] < 0 ? '-' : '+'}{formatNumber(Math.abs(application[`${metricKey}_change_pct`]), {defaultDecimals: 1, thresholdDecimals: {base:0}})}% */}
+ {application[`${metricKey}_change_pct`] < 0 ? '-' : '+'}{Math.abs(application[`${metricKey}_change_pct`]).toLocaleString("en-GB",{maximumFractionDigits:0})}%
+
+ ) :
}
+
@@ -673,6 +561,8 @@ const ApplicationsTable = () => {
)}
useWindowScroll
+ increaseViewportBy={{top:0, bottom: 400}}
+ overscan={50}
/>
{/* */}
diff --git a/components/layout/GridTable.tsx b/components/layout/GridTable.tsx
index ccebf5bd..f36a1400 100644
--- a/components/layout/GridTable.tsx
+++ b/components/layout/GridTable.tsx
@@ -379,6 +379,7 @@ export const GridTableAddressCell = ({
// size 10px font is 6px wide
// size 20px font is 9px wide
+
const fontWidth = useMemo(() => 0.3 * fontSize + 3, [fontSize]);
@@ -392,7 +393,7 @@ export const GridTableAddressCell = ({
return (
-
{
e.stopPropagation();
handleCopyAddress(address);
@@ -411,6 +412,7 @@ export const GridTableAddressCell = ({
// }}
style={{
fontSize: `${fontSize}px`,
+ fontFeatureSettings: "'tnum'",
}}
>