Fix/sidebar and pro banner space (#2438)

* feat(utils): add featurebase utils

* feat(config): add changelog, feedback and roadmap routes

* feat(app): add featurebase script

* feat(docs): add NEXT_PUBLIC_FB_FEEDBACK_URL

* feat(featurebase): add featurebase components

* feat(components): add featurebase components to navbar

* feat(components): add featurebase components to sidebar

* chore(config): remove changelog and feedback at this moment

* fix(components): fb-roadmap-link styles

* chore(components): hide feedback and changelog at this moment

* feat(docs): add NEXT_PUBLIC_FB_FEEDBACK_ORG

* feat(featurebase): add trackEvent & revise props

* fix(sidebar): remove opacity classes from TreeItem component

* fix(docs): pro banner margin

---------

Co-authored-by: աɨռɢӄաօռɢ <wingkwong.code@gmail.com>
This commit is contained in:
Junior Garcia 2024-03-03 16:32:29 -03:00 committed by GitHub
parent 4957f56e86
commit 5528ccd042
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 82 additions and 8 deletions

View File

@ -1,6 +1,6 @@
"use client";
import {FC} from "react";
import {FC, useEffect, useState} from "react";
import {ChevronIcon} from "@nextui-org/shared-icons";
import {CollectionBase, Expandable, MultipleSelection, Node, ItemProps} from "@react-types/shared";
import {BaseItem} from "@nextui-org/aria-utils";
@ -32,6 +32,7 @@ import {FbFeedbackButton} from "@/components/featurebase/fb-feedback-button";
import {FbChangelogButton} from "@/components/featurebase/fb-changelog-button";
import {FbRoadmapLink} from "@/components/featurebase/fb-roadmap-link";
import {openFeedbackWidget} from "@/utils/featurebase";
import emitter from "@/libs/emitter";
export interface Props<T> extends Omit<ItemProps<T>, "title">, Route {
slug?: string;
@ -99,8 +100,6 @@ function TreeItem<T>(props: TreeItemProps<T>) {
"before:w-1",
"before:h-1",
"before:rounded-full",
"opacity-80",
"dark:opacity-60",
);
const {pressProps} = usePress({
@ -284,6 +283,8 @@ export interface DocsSidebarProps {
}
export const DocsSidebar: FC<DocsSidebarProps> = ({routes, slug, tag, className}) => {
const [isProBannerVisible, setIsProBannerVisible] = useState(true);
const expandedKeys = routes?.reduce((keys, route) => {
if (route.defaultOpen) {
keys.push(route.key as string);
@ -292,8 +293,18 @@ export const DocsSidebar: FC<DocsSidebarProps> = ({routes, slug, tag, className}
return keys;
}, [] as string[]);
return (
<div className={clsx("lg:fixed lg:top-20 mt-2 z-0 lg:h-[calc(100vh-121px)]", className)}>
useEffect(() => {
emitter.on("proBannerVisibilityChange", (value) => {
setIsProBannerVisible(value === "visible");
});
return () => {
emitter.off("proBannerVisibilityChange");
};
}, []);
const treeContent = useMemo(() => {
return (
<Tree defaultExpandedKeys={expandedKeys} items={routes || []}>
{(route) => (
<Item
@ -307,6 +318,18 @@ export const DocsSidebar: FC<DocsSidebarProps> = ({routes, slug, tag, className}
</Item>
)}
</Tree>
);
}, [routes]);
return (
<div
className={clsx(
"lg:fixed mt-2 z-0 lg:h-[calc(100vh-121px)]",
isProBannerVisible ? "lg:top-32" : "lg:top-20",
className,
)}
>
{treeContent}
</div>
);
};

View File

@ -1,6 +1,6 @@
"use client";
import {FC, useRef, useEffect} from "react";
import {FC, useRef, useEffect, useState} from "react";
import {clsx} from "@nextui-org/shared-utils";
import {Divider, Spacer} from "@nextui-org/react";
import {ChevronCircleTopLinearIcon} from "@nextui-org/shared-icons";
@ -9,6 +9,7 @@ import scrollIntoView from "scroll-into-view-if-needed";
import {Heading} from "@/libs/docs/utils";
import {useScrollSpy} from "@/hooks/use-scroll-spy";
import {useScrollPosition} from "@/hooks/use-scroll-position";
import emitter from "@/libs/emitter";
export interface DocsTocProps {
headings: Heading[];
@ -22,6 +23,8 @@ const paddingLeftByLevel: Record<number, string> = {
};
export const DocsToc: FC<DocsTocProps> = ({headings}) => {
const [isProBannerVisible, setIsProBannerVisible] = useState(true);
const tocRef = useRef<HTMLDivElement>(null);
const scrollPosition = useScrollPosition(tocRef);
@ -51,10 +54,23 @@ export const DocsToc: FC<DocsTocProps> = ({headings}) => {
}
}, [activeId, activeIndex]);
useEffect(() => {
emitter.on("proBannerVisibilityChange", (value) => {
setIsProBannerVisible(value === "visible");
});
return () => {
emitter.off("proBannerVisibilityChange");
};
}, []);
return (
<div
ref={tocRef}
className="fixed w-full max-w-[12rem] flex flex-col gap-4 text-left top-20 pb-20 h-[calc(100vh-121px)] scrollbar-hide overflow-y-scroll"
className={clsx(
"fixed w-full max-w-[12rem] flex flex-col gap-4 text-left pb-28 h-[calc(100vh-121px)] scrollbar-hide overflow-y-scroll",
isProBannerVisible ? "top-32" : "top-20",
)}
style={{
WebkitMaskImage: `linear-gradient(to top, transparent 0%, #000 100px, #000 ${
scrollPosition > 30 ? "90%" : "100%"

View File

@ -365,7 +365,12 @@ export const Navbar: FC<NavbarProps> = ({children, routes, mobileRoutes = [], sl
</NavbarContent>
<NavbarMenu>
<DocsSidebar className="mt-4" routes={[...mobileRoutes, ...routes]} slug={slug} tag={tag} />
<DocsSidebar
className="mt-4 pt-8"
routes={[...mobileRoutes, ...routes]}
slug={slug}
tag={tag}
/>
{children}
</NavbarMenu>
</NextUINavbar>

View File

@ -3,8 +3,10 @@
import {Icon} from "@iconify/react/dist/offline";
import arrowRightIcon from "@iconify/icons-solar/arrow-right-linear";
import {usePathname} from "next/navigation";
import {useEffect} from "react";
import {trackEvent} from "@/utils/va";
import emitter from "@/libs/emitter";
const hideOnPaths = ["examples"];
@ -19,6 +21,25 @@ export const ProBanner = () => {
const pathname = usePathname();
const shouldBeVisible = !hideOnPaths.some((path) => pathname.includes(path));
useEffect(() => {
if (!shouldBeVisible) return;
// listen to scroll event, dispatch an event when scroll is at the top < 48 px
const handleScroll = () => {
if (window.scrollY < 48) {
emitter.emit("proBannerVisibilityChange", "visible");
} else {
emitter.emit("proBannerVisibilityChange", "hidden");
}
};
window.addEventListener("scroll", handleScroll);
return () => {
window.removeEventListener("scroll", handleScroll);
};
}, [shouldBeVisible]);
if (!shouldBeVisible) return null;
return (

View File

@ -0,0 +1,9 @@
import mitt from "mitt";
type Events = {
proBannerVisibilityChange: "hidden" | "visible";
};
const emitter = mitt<Events>();
export default emitter;