Pārlūkot izejas kodu

feat(insights): implement new CopyButton design

Connor Prussin 8 mēneši atpakaļ
vecāks
revīzija
26f756fe4c

+ 42 - 6
apps/insights/src/components/CopyButton/index.module.scss

@@ -1,8 +1,31 @@
 @use "@pythnetwork/component-library/theme";
 
 .copyButton {
+  display: inline-flex;
+  flex-flow: row nowrap;
+  align-items: center;
+  justify-content: center;
+  gap: 0.5em;
+  background: transparent;
+  transition-property: background-color, color, border-color, outline-color;
+  transition-duration: 100ms;
+  transition-timing-function: linear;
+  border: 1px solid transparent;
+  outline-offset: 0;
+  outline: theme.spacing(1) solid transparent;
+  -webkit-tap-highlight-color: transparent;
+  border-radius: theme.border-radius("base");
+  cursor: pointer;
+  line-height: 150%;
+  padding-left: 0.5em;
+  padding-right: 0.5em;
+  margin-left: -0.5em;
+  margin-right: -0.5em;
+
   .iconContainer {
     position: relative;
+    height: 1em;
+    width: 1em;
 
     .copyIcon {
       opacity: 0.5;
@@ -24,13 +47,26 @@
     }
   }
 
-  &[data-is-copied] .iconContainer {
-    .copyIcon {
-      opacity: 0;
-    }
+  &[data-focus-visible] {
+    border-color: theme.color("focus");
+    outline-color: theme.color("focus-dim");
+  }
 
-    .checkIcon {
-      opacity: 1;
+  &[data-hovered] {
+    background-color: theme.color("button", "outline", "background", "hover");
+  }
+
+  &[data-is-copied] {
+    background-color: theme.color("states", "info", "background");
+
+    .iconContainer {
+      .copyIcon {
+        opacity: 0;
+      }
+
+      .checkIcon {
+        opacity: 1;
+      }
     }
   }
 }

+ 15 - 15
apps/insights/src/components/CopyButton/index.tsx

@@ -3,22 +3,20 @@
 import { Check } from "@phosphor-icons/react/dist/ssr/Check";
 import { Copy } from "@phosphor-icons/react/dist/ssr/Copy";
 import { useLogger } from "@pythnetwork/app-logger";
-import {
-  type Props as ButtonProps,
-  Button,
-} from "@pythnetwork/component-library/Button";
-import type { Button as UnstyledButton } from "@pythnetwork/component-library/unstyled/Button";
+import { Button } from "@pythnetwork/component-library/unstyled/Button";
 import clsx from "clsx";
-import { useCallback, useEffect, useState } from "react";
+import { type ComponentProps, useCallback, useEffect, useState } from "react";
 
 import styles from "./index.module.scss";
 
+const COPY_INDICATOR_TIME = 1000;
+
 type OwnProps = {
   text: string;
 };
 
 type Props = Omit<
-  ButtonProps<typeof UnstyledButton>,
+  ComponentProps<typeof Button>,
   keyof OwnProps | "onPress" | "afterIcon"
 > &
   OwnProps;
@@ -46,7 +44,7 @@ export const CopyButton = ({ text, children, className, ...props }: Props) => {
     if (isCopied) {
       const timeout = setTimeout(() => {
         setIsCopied(false);
-      }, 2000);
+      }, COPY_INDICATOR_TIME);
       return () => {
         clearTimeout(timeout);
       };
@@ -59,16 +57,18 @@ export const CopyButton = ({ text, children, className, ...props }: Props) => {
     <Button
       onPress={copy}
       className={clsx(styles.copyButton, className)}
-      afterIcon={({ className, ...props }) => (
-        <div className={clsx(styles.iconContainer, className)} {...props}>
-          <Copy className={styles.copyIcon} />
-          <Check className={styles.checkIcon} />
-        </div>
-      )}
       {...(isCopied && { "data-is-copied": true })}
       {...props}
     >
-      {children}
+      {(...args) => (
+        <>
+          {typeof children === "function" ? children(...args) : children}
+          <div className={styles.iconContainer}>
+            <Copy className={styles.copyIcon} />
+            <Check className={styles.checkIcon} />
+          </div>
+        </>
+      )}
     </Button>
   );
 };

+ 1 - 0
apps/insights/src/components/EntityList/index.module.scss

@@ -16,6 +16,7 @@
       outline-color 100ms linear,
       background-color 100ms linear;
     -webkit-tap-highlight-color: transparent;
+    cursor: pointer;
 
     &[data-focus-visible] {
       outline: theme.spacing(0.5) solid theme.color("focus");

+ 2 - 2
apps/insights/src/components/PriceFeed/layout.module.scss

@@ -30,8 +30,8 @@
     .rightGroup {
       display: flex;
       flex-flow: row nowrap;
-      align-items: center;
-      gap: theme.spacing(2);
+      align-items: stretch;
+      gap: theme.spacing(4);
 
       & > * {
         flex: 1 1 0;

+ 0 - 2
apps/insights/src/components/PriceFeed/layout.tsx

@@ -68,8 +68,6 @@ export const PriceFeedLayout = async ({ children, params }: Props) => {
           <PriceFeedTag className={styles.priceFeedTag} symbol={feed.symbol} />
           <div className={styles.rightGroup}>
             <FeedKey
-              variant="ghost"
-              size="sm"
               className={styles.feedKey ?? ""}
               feedKey={feed.product.price_account}
             />

+ 0 - 4
apps/insights/src/components/PriceFeeds/price-feeds-card.module.scss

@@ -20,8 +20,4 @@
       display: none;
     }
   }
-
-  .feedKey {
-    margin: -#{theme.button-padding("sm", false)};
-  }
 }

+ 1 - 6
apps/insights/src/components/PriceFeeds/price-feeds-card.tsx

@@ -143,12 +143,7 @@ const ResolvedPriceFeedsCard = ({ priceFeeds, ...props }: Props) => {
             priceFeedName: <PriceFeedTag compact symbol={symbol} />,
             assetClass: <AssetClassTag symbol={symbol} />,
             priceFeedId: (
-              <FeedKey
-                size="xs"
-                variant="ghost"
-                feedKey={key}
-                className={styles.feedKey ?? ""}
-              />
+              <FeedKey feedKey={key} className={styles.feedKey ?? ""} />
             ),
           },
         }),

+ 1 - 1
apps/insights/src/components/Publisher/layout.tsx

@@ -105,7 +105,7 @@ export const PublishersLayout = async ({ children, params }: Props) => {
               items={[
                 { href: "/", label: "Home" },
                 { href: "/publishers", label: "Publishers" },
-                { label: <PublisherKey size="sm" publisherKey={key} /> },
+                { label: <PublisherKey publisherKey={key} /> },
               ]}
             />
           </div>

+ 0 - 9
apps/insights/src/components/PublisherKey/index.module.scss

@@ -1,9 +0,0 @@
-@use "@pythnetwork/component-library/theme";
-
-.publisherKey {
-  @each $size, $values in theme.$button-sizes {
-    &[data-size="#{$size}"] {
-      margin: 0 -#{theme.button-padding($size, true)};
-    }
-  }
-}

+ 2 - 13
apps/insights/src/components/PublisherKey/index.tsx

@@ -1,7 +1,5 @@
-import clsx from "clsx";
 import type { ComponentProps } from "react";
 
-import styles from "./index.module.scss";
 import { CopyButton } from "../CopyButton";
 
 type KeyProps = Omit<
@@ -11,17 +9,8 @@ type KeyProps = Omit<
   publisherKey: string;
 };
 
-export const PublisherKey = ({
-  publisherKey,
-  className,
-  ...props
-}: KeyProps) => (
-  <CopyButton
-    variant="ghost"
-    className={clsx(styles.publisherKey, className)}
-    text={publisherKey}
-    {...props}
-  >
+export const PublisherKey = ({ publisherKey, ...props }: KeyProps) => (
+  <CopyButton text={publisherKey} {...props}>
     {`${publisherKey.slice(0, 4)}...${publisherKey.slice(-4)}`}
   </CopyButton>
 );

+ 2 - 1
apps/insights/src/components/PublisherTag/index.module.scss

@@ -43,7 +43,8 @@
     align-items: flex-start;
 
     .key {
-      margin-bottom: -#{theme.spacing(2)};
+      font-size: theme.font-size("xs");
+      margin-bottom: -0.5em;
     }
   }
 

+ 2 - 3
apps/insights/src/components/PublisherTag/index.tsx

@@ -68,7 +68,7 @@ const Contents = (props: Props) => {
     return props.name ? (
       <div className={styles.name}>{props.name}</div>
     ) : (
-      <PublisherKey publisherKey={props.publisherKey} size="xs" />
+      <PublisherKey publisherKey={props.publisherKey} />
     );
   } else if (props.name) {
     return (
@@ -77,11 +77,10 @@ const Contents = (props: Props) => {
         <PublisherKey
           className={styles.key ?? ""}
           publisherKey={props.publisherKey}
-          size="xs"
         />
       </div>
     );
   } else {
-    return <PublisherKey publisherKey={props.publisherKey} size="sm" />;
+    return <PublisherKey publisherKey={props.publisherKey} />;
   }
 };