diff --git a/src/components/common/table/ScrollableTable.js b/src/components/common/table/ScrollableTable.js
new file mode 100644
index 00000000..ba501ada
--- /dev/null
+++ b/src/components/common/table/ScrollableTable.js
@@ -0,0 +1,199 @@
+import React, { useRef, useEffect, useState } from "react";
+
+export default function ScrollableTable({ children, tableColDimensions }) {
+ const tableScrollRef = useRef(null);
+ const scrollbarRef = useRef(null);
+ const [thumbStyle, setThumbStyle] = useState({ width: "0%", left: "0%" });
+ const [isDragging, setIsDragging] = useState(false);
+ const dragStartRef = useRef({ touchX: 0, scrollLeft: 0 });
+ const [firstColWidth, setFirstColWidth] = useState(199);
+
+ const customStyles = {
+ "--table-col-dimensions": `${tableColDimensions ?? "minmax(0, 1fr)"}`,
+ "--first-col-width": `${firstColWidth}px`,
+ gridTemplateAreas: `"header" "body"`,
+ boxShadow: "2.135px 8.539px 21.347px 0px rgba(0, 0, 54, 0.12)",
+ };
+
+ useEffect(() => {
+ const table = tableScrollRef.current;
+ if (!table) return;
+
+ const updateThumb = () => {
+ const { scrollWidth, clientWidth, scrollLeft } = table;
+ if (scrollWidth <= clientWidth) {
+ setThumbStyle({ width: "100%", left: "0%" });
+ return;
+ }
+
+ const ratio = clientWidth / scrollWidth;
+ const width = `${ratio * 100}%`;
+ const left = `${
+ (scrollLeft / (scrollWidth - clientWidth)) * (100 - ratio * 100)
+ }%`;
+ setThumbStyle({ width, left });
+ };
+
+ const updateFirstColWidth = () => {
+ const firstCell = table.querySelector(
+ ".scrollable-table-grid > div:first-child > div:first-child"
+ );
+ if (firstCell) {
+ setFirstColWidth(firstCell.offsetWidth);
+ }
+ };
+
+ updateThumb();
+ updateFirstColWidth();
+ table.addEventListener("scroll", updateThumb);
+ window.addEventListener("resize", () => {
+ updateThumb();
+ updateFirstColWidth();
+ });
+
+ return () => {
+ table.removeEventListener("scroll", updateThumb);
+ window.removeEventListener("resize", updateThumb);
+ };
+ }, []);
+
+ const handleTouchStart = (e) => {
+ const table = tableScrollRef.current;
+ const scrollbar = scrollbarRef.current;
+ if (!table || !scrollbar) return;
+
+ setIsDragging(true);
+ dragStartRef.current = {
+ touchX: e.touches[0].clientX,
+ scrollLeft: table.scrollLeft,
+ };
+ };
+
+ useEffect(() => {
+ if (!isDragging) return;
+
+ const handleTouchMove = (e) => {
+ e.preventDefault();
+ const table = tableScrollRef.current;
+ const scrollbar = scrollbarRef.current;
+ if (!table || !scrollbar) return;
+
+ const rect = scrollbar.getBoundingClientRect();
+ const x = Math.max(
+ 0,
+ Math.min(e.touches[0].clientX - rect.left, rect.width)
+ );
+ const percentage = x / rect.width;
+
+ const { scrollWidth, clientWidth } = table;
+ table.scrollLeft = percentage * (scrollWidth - clientWidth);
+ };
+
+ const handleTouchEnd = () => {
+ setIsDragging(false);
+ };
+
+ document.addEventListener("touchmove", handleTouchMove, { passive: false });
+ document.addEventListener("touchend", handleTouchEnd);
+
+ return () => {
+ document.removeEventListener("touchmove", handleTouchMove);
+ document.removeEventListener("touchend", handleTouchEnd);
+ };
+ }, [isDragging]);
+
+ return (
+ <>
+
+
+
+ >
+ );
+}
diff --git a/src/components/community-stats/SdvCoreSection.js b/src/components/community-stats/SdvCoreSection.js
index 1d0bce6e..c568def6 100644
--- a/src/components/community-stats/SdvCoreSection.js
+++ b/src/components/community-stats/SdvCoreSection.js
@@ -1,93 +1,26 @@
-import React, { useState, useRef, useEffect } from "react";
-import Table from "../common/table/Table";
+import React from "react";
+import ScrollableTable from "../common/table/ScrollableTable";
import TableHeader from "../common/table/TableHeader";
import TableHeaderCell from "../common/table/TableHeaderCell";
import TableBody from "../common/table/TableBody";
import TableRow from "../common/table/TableRow";
import TableRowCell from "../common/table/TableRowCell";
-import useWindowWidth from "../../hooks/useviewport";
export default function SdvCoreSection({ dependenciesData }) {
const currentYear = new Date().getFullYear().toString();
- const metricKeys = ["toDate", "yearToDate"];
- const metricLabels = ["To date", currentYear];
- const [tableColDimensions, setTableColDimensions] = useState(
- "minmax(134px, 199px) minmax(136px, 488px) minmax(136px, 488px)"
- );
- const width = useWindowWidth();
- const isMobile = width < 768;
-
- const [activeMetricIndex, setActiveMetricIndex] = useState(0);
- const touchStartX = useRef(null);
-
- useEffect(() => {
- setTableColDimensions(
- isMobile
- ? "minmax(134px, 199px) minmax(136px, 488px)"
- : "minmax(134px, 199px) minmax(136px, 488px) minmax(136px, 488px)"
- );
- }, [isMobile]);
-
- const handleSwipeStart = (e) => {
- touchStartX.current = e.touches[0].clientX;
- };
-
- const handleSwipeMove = (e) => {
- if (touchStartX.current === null) return;
- const deltaX = e.touches[0].clientX - touchStartX.current;
- const threshold = 50;
-
- if (deltaX > threshold) {
- setActiveMetricIndex((prev) => Math.max(prev - 1, 0));
- touchStartX.current = null;
- } else if (deltaX < -threshold) {
- setActiveMetricIndex((prev) => Math.min(prev + 1, metricKeys.length - 1));
- touchStartX.current = null;
- }
- };
return (
-
+
SDV Community downloads
-
-
+
+
-
-
-
- {isMobile && (
-
- )}
-
- {isMobile ? (
-
-
- {metricLabels[activeMetricIndex]}
-
-
- ) : (
- <>
- {metricLabels.map((ml) => (
- {ml}
- ))}
- >
- )}
+
+ To date
+ {currentYear}
@@ -96,58 +29,26 @@ export default function SdvCoreSection({ dependenciesData }) {
return (
-
-
-
- {row.name}
-
-
-
- {isMobile && (
-
- )}
-
- {isMobile ? (
-
-
-
- {row[metricKeys[activeMetricIndex]]}
-
-
+
+
+ {row.name}
+
+
+
+
+ {row.toDate}
+
+
+
+
+ {row.yearToDate}
- ) : (
- <>
-
-
- {row.toDate}
-
-
-
-
- {row.yearToDate}
-
-
- >
- )}
+
);
})}
-
+
diff --git a/src/components/community-stats/SdvInNumbersSection.js b/src/components/community-stats/SdvInNumbersSection.js
index 9b4c9b23..c954823d 100644
--- a/src/components/community-stats/SdvInNumbersSection.js
+++ b/src/components/community-stats/SdvInNumbersSection.js
@@ -1,52 +1,15 @@
-import React, { useState, useEffect, useRef } from "react";
-import Table from "../common/table/Table";
+import React, { useState } from "react";
+import ScrollableTable from "../common/table/ScrollableTable";
import TableHeader from "../common/table/TableHeader";
import TableHeaderCell from "../common/table/TableHeaderCell";
import TableBody from "../common/table/TableBody";
import TableRow from "../common/table/TableRow";
import TableRowCell from "../common/table/TableRowCell";
import Tab from "../common/Tab";
-import useWindowWidth from "../../hooks/useviewport";
import CustomPieChart from "./CustomPieChart";
export default function SdvInNumbersSection({ data }) {
const currentYear = new Date().getFullYear().toString();
- const metricKeys = ["toDate", "yearToDate"];
- const metricLabels = ["To date", currentYear];
- const [tableColDimensions, setTableColDimensions] = useState(
- "minmax(199px, 199px) minmax(130px, 488px) minmax(136px, 488px)"
- );
- const width = useWindowWidth();
- const isMobile = width < 768;
-
- const [activeMetricIndex, setActiveMetricIndex] = useState(0);
- const touchStartX = useRef(null);
-
- useEffect(() => {
- setTableColDimensions(
- isMobile
- ? "minmax(192px, 199px) minmax(110px, 488px)"
- : "minmax(199px, 199px) minmax(130px, 488px) minmax(136px, 488px)"
- );
- }, [isMobile]);
-
- const handleSwipeStart = (e) => {
- touchStartX.current = e.touches[0].clientX;
- };
-
- const handleSwipeMove = (e) => {
- if (touchStartX.current === null) return;
- const deltaX = e.touches[0].clientX - touchStartX.current;
- const threshold = 50;
-
- if (deltaX > threshold) {
- setActiveMetricIndex((prev) => Math.max(prev - 1, 0));
- touchStartX.current = null;
- } else if (deltaX < -threshold) {
- setActiveMetricIndex((prev) => Math.min(prev + 1, metricKeys.length - 1));
- touchStartX.current = null;
- }
- };
const [tabs, setTabs] = useState([
{ label: "Visualize", isActive: true },
@@ -98,41 +61,11 @@ export default function SdvInNumbersSection({ data }) {
{activeTab.label === "Downloads" && (
-
+
-
-
-
- {isMobile && (
-
- )}
-
- {isMobile ? (
-
-
- {metricLabels[activeMetricIndex]}
-
-
- ) : (
- <>
- {metricLabels.map((ml) => (
- {ml}
- ))}
- >
- )}
+
+ To date
+ {currentYear}
@@ -141,58 +74,26 @@ export default function SdvInNumbersSection({ data }) {
return (
-
-
-
- {row.name}
-
-
-
- {isMobile && (
-
- )}
-
- {isMobile ? (
-
-
-
- {row[metricKeys[activeMetricIndex]]}
-
-
+
+
+ {row.name}
+
+
+
+
+ {row.toDate}
+
+
+
+
+ {row.yearToDate}
- ) : (
- <>
-
-
- {row.toDate}
-
-
-
-
- {row.yearToDate}
-
-
- >
- )}
+
);
})}
-
+
)}
{activeTab.label === "Visualize" &&
}