Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 26 additions & 10 deletions apps/developer-hub/src/components/Root/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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" },
Expand All @@ -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 changes: 29 additions & 0 deletions apps/developer-hub/src/components/search-button.tsx
Original file line number Diff line number Diff line change
@@ -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"
/>
</>
);
};
21 changes: 5 additions & 16 deletions apps/developer-hub/src/config/layout.config.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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,
},
};
16 changes: 0 additions & 16 deletions apps/insights/src/components/Root/search-button.module.scss
Original file line number Diff line number Diff line change
@@ -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;
Expand Down
40 changes: 2 additions & 38 deletions apps/insights/src/components/Root/search-button.tsx
Original file line number Diff line number Diff line change
@@ -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,
Expand All @@ -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";
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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 = ({
Expand Down
17 changes: 17 additions & 0 deletions packages/component-library/src/SearchButton/index.module.scss
Original file line number Diff line number Diff line change
@@ -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 changes: 44 additions & 0 deletions packages/component-library/src/SearchButton/index.stories.tsx
Original file line number Diff line number Diff line change
@@ -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 changes: 67 additions & 0 deletions packages/component-library/src/SearchButton/index.tsx
Original file line number Diff line number Diff line change
@@ -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>
);
};
Loading