Selaa lähdekoodia

fix: feeds table

Alexandru Cambose 1 päivä sitten
vanhempi
sitoutus
837a84664a

+ 26 - 161
apps/developer-hub/src/components/SponsoredFeedsTable/index.module.scss

@@ -1,19 +1,26 @@
+@use "@pythnetwork/component-library/theme";
+
 .container {
   margin: 1.5rem 0;
 }
 
+.table {
+  max-height: 400px;
+  overflow-y: auto;
+  border-bottom-left-radius: theme.border-radius("xl");
+  border-bottom-right-radius: theme.border-radius("xl");
+}
+
 .introText {
   margin-bottom: 1rem;
-  color: var(--color-gray-700);
+  color: theme.color("paragraph");
   font-size: 1rem;
 }
 
 .tableWrapper {
-  border: 1px solid var(--color-gray-200);
-  border-radius: 0.75rem;
-  background-color: var(--color-white);
-  box-shadow: 0 1px 2px rgb(15 23 42 / 8%);
-  overflow: hidden;
+  border: 1px solid theme.pallette-color("gray", 700);
+  border-radius: theme.border-radius("xl");
+  background-color: theme.color("background", "primary");
 }
 
 .summaryBar {
@@ -21,197 +28,55 @@
   flex-wrap: wrap;
   gap: 1.25rem;
   padding: 0.75rem 1rem;
-  background-color: var(--color-blue-50);
-  border-bottom: 1px solid var(--color-gray-200);
 }
 
 .summaryItem {
   display: inline-flex;
   align-items: center;
   gap: 0.5rem;
-  color: var(--color-gray-700);
+  color: theme.color("paragraph");
   font-size: 0.875rem;
 }
 
 .summaryLabel {
-  font-weight: 600;
+  @include theme.text("base", "medium");
 }
 
 .summaryCount {
-  color: var(--color-gray-500);
+  color: theme.pallette-color("gray", 500);
 }
 
 .statusDot {
   width: 0.5rem;
   height: 0.5rem;
-  border-radius: 9999px;
-  background-color: var(--color-gray-400);
+  border-radius: theme.border-radius("full");
   flex-shrink: 0;
 }
 
 .statusDotDefault {
-  background-color: var(--color-green-500);
+  background-color: theme.pallette-color("green", 500);
 }
 
 .statusDotException {
-  background-color: var(--color-orange-500);
-}
-
-.tableScroll {
-  max-height: 24rem;
-  overflow: auto;
-}
-
-.table {
-  width: 100%;
-  border-collapse: collapse;
-  font-size: 0.875rem;
-  min-width: 620px;
-}
-
-.tableHead {
-  position: sticky;
-  top: 0;
-  background-color: var(--color-gray-50);
-  border-bottom: 1px solid var(--color-gray-200);
-  z-index: 2;
-}
-
-.headerCell {
-  padding: 0.75rem 0.875rem;
-  text-align: left;
-  font-weight: 600;
-  color: var(--color-gray-900);
-  border-bottom: 1px solid var(--color-gray-200);
-}
-
-.tableBody {
-  background-color: var(--color-white);
-}
-
-.tableRow {
-  border-bottom: 1px solid var(--color-gray-100);
-  transition: background-color 0.2s ease;
-}
-
-.tableRow:hover {
-  background-color: var(--color-gray-50);
-}
-
-.nameCell,
-.accountCell,
-.idCell,
-.paramsCell {
-  padding: 0.75rem 0.875rem;
-  vertical-align: top;
-  color: var(--color-gray-800);
+  background-color: theme.pallette-color("orange", 500);
 }
 
 .nameLabel {
-  font-weight: 600;
-  color: var(--color-gray-900);
-}
-
-.copyWrapper {
-  display: inline-flex;
-  align-items: flex-start;
-  gap: 0.5rem;
-}
-
-.code {
-  font-family: var(--py-font-family-mono, "IBM Plex Mono", monospace);
-  font-size: 0.75rem;
-  line-height: 1.4;
-  color: var(--color-gray-600);
-  word-break: break-all;
+  @include theme.text("base", "medium");
 }
 
 .updateParams {
   display: flex;
-  align-items: flex-start;
+  align-items: center;
   gap: 0.5rem;
-}
 
-.updateParamsText {
-  font-size: 0.75rem;
-  line-height: 1.5;
-  color: var(--color-gray-700);
-}
-
-.updateParamsDefault .updateParamsText {
-  color: var(--color-gray-700);
+  @include theme.text("sm", "light");
 }
 
-.updateParamsException .updateParamsText {
-  color: var(--color-orange-600);
+.updateParamsText {
+  color: theme.color("paragraph");
 }
 
-@media (prefers-color-scheme: dark) {
-  .introText {
-    color: var(--color-gray-300);
-  }
-
-  .tableWrapper {
-    border-color: var(--color-gray-700);
-    background-color: var(--color-gray-900);
-    box-shadow: none;
-  }
-
-  .summaryBar {
-    background-color: rgb(30 64 175 / 25%);
-    border-color: var(--color-gray-700);
-  }
-
-  .summaryItem {
-    color: var(--color-gray-300);
-  }
-
-  .summaryCount {
-    color: var(--color-gray-500);
-  }
-
-  .tableHead {
-    background-color: var(--color-gray-800);
-    border-color: var(--color-gray-700);
-  }
-
-  .headerCell {
-    color: var(--color-gray-100);
-    border-color: var(--color-gray-700);
-  }
-
-  .tableBody {
-    background-color: var(--color-gray-900);
-  }
-
-  .tableRow {
-    border-color: rgb(148 163 184 / 15%);
-  }
-
-  .tableRow:hover {
-    background-color: rgb(148 163 184 / 10%);
-  }
-
-  .nameCell,
-  .accountCell,
-  .idCell,
-  .paramsCell {
-    color: var(--color-gray-200);
-  }
-
-  .nameLabel {
-    color: var(--color-gray-100);
-  }
-
-  .code {
-    color: var(--color-gray-400);
-  }
-
-  .updateParamsText {
-    color: var(--color-gray-300);
-  }
-
-  .updateParamsException .updateParamsText {
-    color: var(--color-orange-400);
-  }
+.updateParamsException {
+  color: theme.pallette-color("orange", 600);
 }

+ 94 - 60
apps/developer-hub/src/components/SponsoredFeedsTable/index.tsx

@@ -1,5 +1,9 @@
 import { CopyButton } from "@pythnetwork/component-library/CopyButton";
+import type { RowConfig } from "@pythnetwork/component-library/Table";
+import { Table } from "@pythnetwork/component-library/Table";
 import clsx from "clsx";
+import type { ReactNode } from "react";
+import { useMemo } from "react";
 
 import styles from "./index.module.scss";
 
@@ -22,6 +26,9 @@ type UpdateParamsProps = {
   isDefault: boolean;
 };
 
+const truncateHex = (value: string) =>
+  `${value.slice(0, 6)}...${value.slice(-4)}`;
+
 const formatTimeDifference = (
   seconds: number,
 ): { value: number; unit: string } => {
@@ -77,17 +84,6 @@ export const SponsoredFeedsTable = ({
   feeds,
   networkName,
 }: SponsoredFeedsTableProps) => {
-  if (feeds.length === 0) {
-    return (
-      <div className={styles.container}>
-        <p className={styles.introText}>
-          No sponsored price feeds are currently available for{" "}
-          <strong>{networkName}</strong>.
-        </p>
-      </div>
-    );
-  }
-
   const paramCounts: Record<string, number> = {};
   for (const feed of feeds) {
     const key = formatUpdateParams(feed);
@@ -104,6 +100,85 @@ export const SponsoredFeedsTable = ({
 
   const hasAccountAddress = feeds.some((feed) => !!feed.account_address);
 
+
+  const columns = useMemo(
+    () => [
+      {
+        id: "name" as const,
+        name: "Name",
+        isRowHeader: true,
+        alignment: "left" as const,
+      },
+      ...(hasAccountAddress
+        ? [
+            {
+              id: "accountAddress" as const,
+              name: "Account Address",
+              alignment: "left" as const,
+            },
+          ]
+        : []),
+      {
+        id: "priceFeedId" as const,
+        name: "Price Feed Id",
+        alignment: "left" as const,
+      },
+      {
+        id: "updateParameters" as const,
+        name: "Update Parameters",
+        alignment: "left" as const,
+      },
+    ],
+    [hasAccountAddress],
+  );
+
+  const rows = useMemo(
+    () =>
+      feeds.map((feed) => {
+        const formattedParams = formatUpdateParams(feed);
+        const isDefault = formattedParams === defaultParams;
+
+        const rowData: {
+          name: ReactNode;
+          priceFeedId: ReactNode;
+          updateParameters: ReactNode;
+          accountAddress: ReactNode | undefined;
+        } = {
+          name: <span className={styles.nameLabel}>{feed.alias}</span>,
+          priceFeedId: (
+            <CopyButton text={feed.id}>{truncateHex(feed.id)}</CopyButton>
+          ),
+          updateParameters: <UpdateParams feed={feed} isDefault={isDefault} />,
+          accountAddress: undefined,
+        };
+
+        if (hasAccountAddress) {
+          rowData.accountAddress = feed.account_address ? (
+            <CopyButton text={feed.account_address}>
+              {truncateHex(feed.account_address)}
+            </CopyButton>
+          ) : undefined;
+        }
+
+        return {
+          id: feed.id,
+          data: rowData,
+        };
+      }),
+    [feeds, defaultParams, hasAccountAddress],
+  );
+
+  if (feeds.length === 0) {
+    return (
+      <div className={styles.container}>
+        <p className={styles.introText}>
+          No sponsored price feeds are currently available for{" "}
+          <strong>{networkName}</strong>.
+        </p>
+      </div>
+    );
+  }
+
   return (
     <div className={styles.container}>
       <p className={styles.introText}>
@@ -136,55 +211,14 @@ export const SponsoredFeedsTable = ({
             ))}
         </div>
 
-        <div className={styles.tableScroll}>
-          <table className={styles.table}>
-            <thead className={styles.tableHead}>
-              <tr>
-                <th className={styles.headerCell}>Name</th>
-                {hasAccountAddress ? (
-                  <th className={styles.headerCell}>Account Address</th>
-                ) : undefined}
-                <th className={styles.headerCell}>Price Feed Id</th>
-                <th className={styles.headerCell}>Update Parameters</th>
-              </tr>
-            </thead>
-            <tbody className={styles.tableBody}>
-              {feeds.map((feed) => {
-                const formattedParams = formatUpdateParams(feed);
-                const isDefault = formattedParams === defaultParams;
-
-                return (
-                  <tr key={feed.id} className={styles.tableRow}>
-                    <td className={styles.nameCell}>
-                      <span className={styles.nameLabel}>{feed.alias}</span>
-                    </td>
-                    {hasAccountAddress ? (
-                      <td className={styles.accountCell}>
-                        {feed.account_address ? (
-                          <div className={styles.copyWrapper}>
-                            <code className={styles.code}>
-                              {feed.account_address}
-                            </code>
-                            <CopyButton text={feed.account_address} iconOnly />
-                          </div>
-                        ) : undefined}
-                      </td>
-                    ) : undefined}
-                    <td className={styles.idCell}>
-                      <div className={styles.copyWrapper}>
-                        <code className={styles.code}>{feed.id}</code>
-                        <CopyButton text={feed.id} iconOnly />
-                      </div>
-                    </td>
-                    <td className={styles.paramsCell}>
-                      <UpdateParams feed={feed} isDefault={isDefault} />
-                    </td>
-                  </tr>
-                );
-              })}
-            </tbody>
-          </table>
-        </div>
+        <Table
+          label="Sponsored Feeds"
+          fill
+          columns={columns}
+          rows={rows}
+          stickyHeader="top"
+          className={clsx("not-prose", styles.table)}
+        />
       </div>
     </div>
   );