Преглед изворни кода

chore(dev-hub)-header Improvements (#3002)

* chore(dev-hub)-header Improvements

* requested changes

* fix: wrong provider order

* feat: search button component

* fix: nit

* fix: theme import

* fix: formatting

* fix: props

* feat: default content for search component

* fix: cache params

* fix: remove unused imports

---------

Co-authored-by: Alexandru Cambose <alexcambose@yahoo.com>
Aditya Arora пре 2 месеци
родитељ
комит
5aebc70205

+ 26 - 10
apps/developer-hub/src/components/Root/index.tsx

@@ -2,13 +2,15 @@ import { AppShell } from "@pythnetwork/component-library/AppShell";
 import { RootProvider as FumadocsRootProvider } from "fumadocs-ui/provider";
 import { NuqsAdapter } from "nuqs/adapters/next/app";
 import type { ReactNode } from "react";
-import "./global.css";
 
 import {
   AMPLITUDE_API_KEY,
   ENABLE_ACCESSIBILITY_REPORTING,
   GOOGLE_ANALYTICS_ID,
 } from "../../config/server";
+import { SearchButton } from "../search-button";
+
+import "./global.css";
 
 export const TABS = [
   { segment: "", children: "Home" },
@@ -23,14 +25,28 @@ type Props = {
 };
 
 export const Root = ({ children }: Props) => (
-  <AppShell
-    appName="Developer Hub"
-    amplitudeApiKey={AMPLITUDE_API_KEY}
-    googleAnalyticsId={GOOGLE_ANALYTICS_ID}
-    enableAccessibilityReporting={ENABLE_ACCESSIBILITY_REPORTING}
-    providers={[NuqsAdapter]}
-    tabs={TABS}
+  <FumadocsRootProvider
+    search={{
+      enabled: true,
+      options: {
+        api: "/api/search",
+      },
+    }}
   >
-    <FumadocsRootProvider>{children}</FumadocsRootProvider>
-  </AppShell>
+    <AppShell
+      appName="Developer Hub"
+      amplitudeApiKey={AMPLITUDE_API_KEY}
+      googleAnalyticsId={GOOGLE_ANALYTICS_ID}
+      enableAccessibilityReporting={ENABLE_ACCESSIBILITY_REPORTING}
+      extraCta={<SearchButton />}
+      mainCta={{
+        label: "Insights",
+        href: "https://insights.pyth.network/",
+      }}
+      providers={[NuqsAdapter]}
+      tabs={TABS}
+    >
+      {children}
+    </AppShell>
+  </FumadocsRootProvider>
 );

+ 29 - 0
apps/developer-hub/src/components/search-button.tsx

@@ -0,0 +1,29 @@
+"use client";
+
+import { SearchButton as SearchButtonComponent } from "@pythnetwork/component-library/SearchButton";
+import DefaultSearchDialog from "fumadocs-ui/components/dialog/search-default";
+import { useCallback, useState } from "react";
+
+export const SearchButton = () => {
+  const [open, setOpen] = useState(false);
+
+  const handleSearch = useCallback(() => {
+    setOpen(true);
+  }, []);
+
+  return (
+    <>
+      <SearchButtonComponent
+        size="sm"
+        smallScreenContent="Search"
+        largeScreenContent="Search"
+        onClick={handleSearch}
+      />
+      <DefaultSearchDialog
+        open={open}
+        onOpenChange={setOpen}
+        api="/api/search"
+      />
+    </>
+  );
+};

+ 5 - 16
apps/developer-hub/src/config/layout.config.tsx

@@ -5,31 +5,20 @@ import { source } from "../source";
 
 export const baseOptions: BaseLayoutProps = {
   nav: {
-    enabled: false,
+    enabled: true,
   },
   themeSwitch: {
     enabled: false,
   },
+  searchToggle: {
+    enabled: false,
+  },
 };
 
 export const docsOptions: DocsLayoutProps = {
   ...baseOptions,
   tree: source.pageTree,
   sidebar: {
-    tabs: {
-      transform(option, node) {
-        const meta = source.getNodeMeta(node);
-        if (!meta || !node.icon) return option;
-
-        return {
-          ...option,
-          icon: (
-            <div className="[&_svg]:size-6.5 md:[&_svg]:size-5">
-              {node.icon}
-            </div>
-          ),
-        };
-      },
-    },
+    tabs: false,
   },
 };

+ 0 - 16
apps/insights/src/components/Root/search-button.module.scss

@@ -1,21 +1,5 @@
 @use "@pythnetwork/component-library/theme";
 
-.searchButton {
-  .largeScreenSearchButton {
-    display: none;
-
-    @include theme.breakpoint("md") {
-      display: inline-flex;
-    }
-  }
-
-  .smallScreenSearchButton {
-    @include theme.breakpoint("md") {
-      display: none;
-    }
-  }
-}
-
 .searchDialogContents {
   gap: theme.spacing(1);
   display: flex;

+ 2 - 38
apps/insights/src/components/Root/search-button.tsx

@@ -1,14 +1,13 @@
 "use client";
 
-import { MagnifyingGlass } from "@phosphor-icons/react/dist/ssr/MagnifyingGlass";
 import { XCircle } from "@phosphor-icons/react/dist/ssr/XCircle";
 import { Badge } from "@pythnetwork/component-library/Badge";
 import type { Props as ButtonProps } from "@pythnetwork/component-library/Button";
 import { Button } from "@pythnetwork/component-library/Button";
 import { NoResults } from "@pythnetwork/component-library/NoResults";
+import { SearchButton as SearchButtonComponent } from "@pythnetwork/component-library/SearchButton";
 import { SearchInput } from "@pythnetwork/component-library/SearchInput";
 import { SingleToggleGroup } from "@pythnetwork/component-library/SingleToggleGroup";
-import { Skeleton } from "@pythnetwork/component-library/Skeleton";
 import {
   ListLayout,
   Virtualizer,
@@ -23,7 +22,6 @@ import { useLogger } from "@pythnetwork/component-library/useLogger";
 import { matchSorter } from "match-sorter";
 import type { ReactNode } from "react";
 import { useCallback, useEffect, useMemo, useState } from "react";
-import { useIsSSR } from "react-aria";
 
 import { Cluster, ClusterToName } from "../../services/pyth";
 import { AssetClassBadge } from "../AssetClassBadge";
@@ -74,31 +72,7 @@ const ResolvedSearchButton = (props: ResolvedSearchButtonProps) => {
 
 const SearchButtonImpl = (
   props: Omit<ButtonProps<typeof UnstyledButton>, "children">,
-) => (
-  <div className={styles.searchButton}>
-    <Button
-      className={styles.largeScreenSearchButton ?? ""}
-      variant="outline"
-      beforeIcon={<MagnifyingGlass />}
-      size="sm"
-      rounded
-      {...props}
-    >
-      <SearchShortcutText />
-    </Button>
-    <Button
-      className={styles.smallScreenSearchButton ?? ""}
-      hideText
-      variant="ghost"
-      beforeIcon={<MagnifyingGlass />}
-      size="sm"
-      rounded
-      {...props}
-    >
-      Search
-    </Button>
-  </div>
-);
+) => <SearchButtonComponent size="sm" {...props} />;
 
 const useSearchDrawer = ({ feeds, publishers }: ResolvedSearchButtonProps) => {
   const drawer = useDrawer();
@@ -152,16 +126,6 @@ const useSearchHotkey = (openSearchDrawer: () => void) => {
   }, [handleKeyDown]);
 };
 
-const SearchShortcutText = () => {
-  const isSSR = useIsSSR();
-  return isSSR ? <Skeleton width={7} /> : <SearchTextImpl />;
-};
-
-const SearchTextImpl = () => {
-  const isMac = useMemo(() => navigator.userAgent.includes("Mac"), []);
-  return isMac ? "⌘ K" : "Ctrl K";
-};
-
 type SearchDialogContentsProps = ResolvedSearchButtonProps;
 
 const SearchDialogContents = ({

+ 17 - 0
packages/component-library/src/SearchButton/index.module.scss

@@ -0,0 +1,17 @@
+@use "../theme";
+
+.searchButton {
+  .largeScreenSearchButton {
+    display: none;
+
+    @include theme.breakpoint("md") {
+      display: inline-flex;
+    }
+  }
+
+  .smallScreenSearchButton {
+    @include theme.breakpoint("md") {
+      display: none;
+    }
+  }
+}

+ 44 - 0
packages/component-library/src/SearchButton/index.stories.tsx

@@ -0,0 +1,44 @@
+import * as icons from "@phosphor-icons/react/dist/ssr";
+import type { Meta, StoryObj } from "@storybook/react";
+
+import { SearchButton as SearchButtonComponent } from "./index.jsx";
+import { SIZES } from "../Button/index.jsx";
+
+const iconControl = {
+  control: "select",
+  options: Object.keys(icons),
+  mapping: Object.fromEntries(
+    Object.entries(icons).map(([iconName, Icon]) => [
+      iconName,
+      <Icon key={iconName} weights={new Map()} />,
+    ]),
+  ),
+} as const;
+
+const meta = {
+  component: SearchButtonComponent,
+  argTypes: {
+    size: {
+      control: "inline-radio",
+      options: SIZES,
+      table: {
+        category: "Variant",
+      },
+    },
+    beforeIcon: {
+      ...iconControl,
+      table: {
+        category: "Contents",
+      },
+    },
+  },
+} satisfies Meta<typeof SearchButtonComponent>;
+export default meta;
+
+export const SearchButton = {
+  args: {
+    largeScreenContent: "Search",
+    smallScreenContent: "Search",
+    size: "sm",
+  },
+} satisfies StoryObj<typeof SearchButtonComponent>;

+ 67 - 0
packages/component-library/src/SearchButton/index.tsx

@@ -0,0 +1,67 @@
+"use client";
+
+import { MagnifyingGlass } from "@phosphor-icons/react/dist/ssr/MagnifyingGlass";
+import clsx from "clsx";
+import type { ReactNode } from "react";
+import { useMemo } from "react";
+import { useIsSSR } from "react-aria";
+import { Button as BaseButton } from "react-aria-components";
+
+import type { Props as ButtonProps } from "../Button";
+import { Button } from "../Button";
+import { Skeleton } from "../Skeleton";
+import styles from "./index.module.scss";
+
+type OwnProps = {
+  largeScreenContent?: ReactNode;
+  smallScreenContent?: ReactNode;
+};
+
+type Props = Pick<
+  ButtonProps<typeof BaseButton>,
+  "beforeIcon" | "size" | "onClick" | "className" | "isPending"
+> &
+  OwnProps;
+
+const SearchShortcutText = () => {
+  const isSSR = useIsSSR();
+  return isSSR ? <Skeleton width={7} /> : <SearchTextImpl />;
+};
+
+const SearchTextImpl = () => {
+  const isMac = useMemo(() => navigator.userAgent.includes("Mac"), []);
+  return isMac ? "⌘ K" : "Ctrl K";
+};
+
+export const SearchButton = ({
+  beforeIcon,
+  largeScreenContent,
+  smallScreenContent,
+  ...props
+}: Props) => {
+  return (
+    <div className={styles.searchButton}>
+      <Button
+        className={clsx(styles.largeScreenSearchButton, props.className)}
+        variant="outline"
+        beforeIcon={beforeIcon ?? <MagnifyingGlass />}
+        size="sm"
+        rounded
+        {...props}
+      >
+        {largeScreenContent ?? <SearchShortcutText />}
+      </Button>
+      <Button
+        className={clsx(styles.smallScreenSearchButton, props.className)}
+        hideText
+        variant="ghost"
+        beforeIcon={beforeIcon ?? <MagnifyingGlass />}
+        size="sm"
+        rounded
+        {...props}
+      >
+        {smallScreenContent ?? <SearchShortcutText />}
+      </Button>
+    </div>
+  );
+};