Skip to content
This repository was archived by the owner on Feb 10, 2025. It is now read-only.

feat: refactor Navbar with NavbarGeneric #121

Merged
merged 5 commits into from
Jan 24, 2025
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
1 change: 1 addition & 0 deletions eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export default tseslint.config(
globals: globals.browser,
},
rules: {
"@typescript-eslint/no-empty-object-type": "off",
"@typescript-eslint/no-unused-vars": "warn",
"@typescript-eslint/no-non-null-assertion": "warn",
"@typescript-eslint/no-explicit-any": "warn",
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"dev": "pnpm storybook",
"test": "test-storybook",
"build": "tsc && vite build",
"build:watch": "tsc && vite build --watch",
"lint": "eslint . --report-unused-disable-directives --max-warnings 0",
"format": "prettier --write --ignore-unknown .",
"preview": "vite",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Meta, Title, Primary, Stories, Controls, Story } from "@storybook/blocks";

import { Navbar } from "./Navbar";
import * as NavbarStories from "./Navbar.stories";

Expand All @@ -20,4 +21,4 @@ The Navbar component displays a navigation bar with optional logos and text. It

# Other variations

<Stories/>
<Stories />
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { Navbar } from "./Navbar";
// Adjust the path as needed

const meta = {
title: "Primitives/Navbar",
title: "Components/Navbar",
component: Navbar,
args: {
text: {
Expand Down
46 changes: 46 additions & 0 deletions src/components/Navbar/Navbar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
"use client";

import * as React from "react";

import { NavbarGeneric } from "./components/NavbarGeneric";
import { NavbarLogo, NavbarLogoProps } from "./components/NavbarLogo";
import {
NavbarCenterSection,
NavbarEndSection,
NavbarStartSection,
} from "./components/NavbarSections";
import { NavbarSeparator } from "./components/NavbarSeparator";
import { NavbarTitle, NavbarTitleProps } from "./components/NavbarTitle";

export interface NavbarProps {
className?: string;
primaryLogo?: NavbarLogoProps;
secondaryLogo?: NavbarLogoProps;
showDivider?: boolean;
text?: NavbarTitleProps;
children?: React.ReactNode;
}

export const Navbar = ({
className,
primaryLogo,
secondaryLogo,
showDivider = true,
text,
children,
}: NavbarProps) => {
return (
<NavbarGeneric behavior="sticky" className={className}>
<NavbarStartSection>
<NavbarLogo {...primaryLogo} />
{showDivider && <NavbarSeparator />}
<div className="flex items-center gap-2">
{secondaryLogo && <NavbarLogo {...secondaryLogo} />}
{text && <NavbarTitle {...text} />}
</div>
</NavbarStartSection>
<NavbarCenterSection>{"hola"}</NavbarCenterSection>
<NavbarEndSection>{children}</NavbarEndSection>
</NavbarGeneric>
);
};
89 changes: 89 additions & 0 deletions src/components/Navbar/components/NavbarGeneric.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import { Meta, Story, Canvas, Controls } from "@storybook/blocks";

import * as NavbarGenericStories from "./NavbarGeneric.stories";

<Meta of={NavbarGenericStories} />

# NavbarGeneric

A flexible navbar component that handles layout and positioning of navbar sections.

Key features:

- Section order doesn't matter - they'll always render in Start, Center, End order
- Sections can be omitted - empty sections will be created automatically
- Configurable behavior (static, sticky, fixed)
- Optional backdrop blur effect
- Optional shadow effect

## Usage

```tsx
import {
NavbarGeneric,
NavbarStartSection,
NavbarCenterSection,
NavbarEndSection,
} from "@/components/Navbar";

export const MyNavbar = () => (
<NavbarGeneric>
<NavbarStartSection>
<Logo />
</NavbarStartSection>
<NavbarCenterSection>
<Navigation />
</NavbarCenterSection>
<NavbarEndSection>
<UserMenu />
</NavbarEndSection>
</NavbarGeneric>
);
```

```tsx
// Example with all props
import { NavbarGeneric } from "@/components/Navbar";

export const MyNavbar = () => (
<NavbarGeneric behavior="sticky" blur={true} shadow={false}>
{/* Your navbar content */}
</NavbarGeneric>
);
```

```tsx
import {
NavbarGeneric,
NavbarStartSection,
NavbarCenterSection,
NavbarEndSection,
} from "@/components/Navbar";

export const MyNavbar = () => (
<NavbarGeneric behavior="sticky">
{/* Order doesn't matter */}
<NavbarEndSection>End</NavbarEndSection>
<NavbarStartSection>Start</NavbarStartSection>

{/* Center section can be omitted */}
</NavbarGeneric>
);
```

## Props

The NavbarGeneric component accepts the following props:

- `behavior` - Controls the positioning behavior of the navbar
- `static` - Normal document flow
- `sticky` - Sticks to the top when scrolling (default)
- `fixed` - Fixed to the viewport
- `blur` - Enables backdrop blur effect (default: true)
- `shadow` - Adds a subtle shadow below the navbar (default: true)

<Controls />

### Default

<Canvas of={NavbarGenericStories.Default} />
126 changes: 126 additions & 0 deletions src/components/Navbar/components/NavbarGeneric.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
import * as React from "react";

import type { Meta, StoryObj } from "@storybook/react";

import { NavbarGeneric } from "./NavbarGeneric";
import { NavbarCenterSection, NavbarEndSection, NavbarStartSection } from "./NavbarSections";

const meta = {
title: "Components/NavbarGeneric",
component: NavbarGeneric,
argTypes: {
behavior: {
control: "radio",
options: ["static", "sticky", "fixed"],
description: "Behavior of the navbar",
table: {
defaultValue: { summary: "sticky" },
},
},
},
} satisfies Meta<typeof NavbarGeneric>;

export default meta;

type Story = StoryObj<typeof NavbarGeneric>;

const DefaultContent = () => (
<>
<NavbarStartSection>
<div className="rounded-sm bg-grey-500 p-2">Start Content</div>
</NavbarStartSection>
<NavbarCenterSection>
<div className="rounded-sm bg-grey-500 p-2">Center Content</div>
</NavbarCenterSection>
<NavbarEndSection>
<div className="rounded-sm bg-grey-500 p-2">End Content</div>
</NavbarEndSection>
</>
);

const ScrollDecorator = (Story: React.ComponentType) => (
<div className="h-[200vh]">
<Story />
<div className="p-4">Scroll content</div>
</div>
);

export const Default: Story = {
args: {
children: <DefaultContent />,
},
};

export const Static: Story = {
args: {
behavior: "static",
children: <DefaultContent />,
},
argTypes: {
children: {
table: {
disable: true,
},
},
},
decorators: [ScrollDecorator],
};

export const Sticky: Story = {
args: {
behavior: "sticky",
children: <DefaultContent />,
},
argTypes: {
children: {
table: {
disable: true,
},
},
},
decorators: [ScrollDecorator],
};

export const Fixed: Story = {
args: {
behavior: "fixed",
children: <DefaultContent />,
},
argTypes: {
children: {
table: {
disable: true,
},
},
},
decorators: [ScrollDecorator],
};

export const RandomOrder: Story = {
render: (args) => (
<NavbarGeneric {...args}>
<NavbarEndSection>
<div className="bg-grey-500 p-2">End First</div>
</NavbarEndSection>
<NavbarStartSection>
<div className="bg-grey-500 p-2">Start Second</div>
</NavbarStartSection>
<NavbarCenterSection>
<div className="bg-grey-500 p-2">Center Last</div>
</NavbarCenterSection>
</NavbarGeneric>
),
};

export const OmittedSections: Story = {
render: (args) => (
<NavbarGeneric {...args}>
<NavbarStartSection>
<div className="bg-grey-500 p-2">Only Start</div>
</NavbarStartSection>
<NavbarEndSection>
<div className="bg-grey-500 p-2">And End</div>
</NavbarEndSection>
</NavbarGeneric>
),
};
65 changes: 65 additions & 0 deletions src/components/Navbar/components/NavbarGeneric.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
"use client";

import * as React from "react";

import { tv, type VariantProps } from "tailwind-variants";

import { cn } from "@/lib";

import { filterAndSortChildren } from "../utils/filterAndSortChildren";

const navbarVariants = tv({
base: "z-50 flex h-16 w-full items-center justify-between bg-white/5 px-20 py-2.5",
variants: {
behavior: {
static: "",
sticky: "sticky top-0",
fixed: "fixed top-0",
},
blur: {
true: "backdrop-blur-[22px]",
false: "",
},
shadow: {
true: "shadow-[0px_4px_24px_0px_rgba(0,0,0,0.08)]",
false: "",
},
},
defaultVariants: {
behavior: "sticky",
blur: true,
shadow: true,
},
});

export interface NavbarGenericProps
extends React.HTMLAttributes<HTMLDivElement>,
VariantProps<typeof navbarVariants> {
/** Children should be NavbarStartSection, NavbarCenterSection, or NavbarEndSection components */
children?: React.ReactNode;
/** Position of the navbar: static, sticky, or fixed
* @default sticky
*/
behavior?: "static" | "sticky" | "fixed";
/** Enables a backdrop blur effect behind the navbar. Useful for transparent or semi-transparent navbars.
* @default true
*/
blur?: boolean;
/** Adds a subtle shadow below the navbar to create depth and separation from the content.
* @default true
*/
shadow?: boolean;
}

export const NavbarGeneric = React.forwardRef<HTMLDivElement, NavbarGenericProps>(
({ className, behavior, children, blur, shadow, ...props }, ref) => {
const navbarStyle = navbarVariants({ behavior, blur, shadow });
const filteredChildren = filterAndSortChildren(children);
return (
<div ref={ref} className={cn(navbarStyle, className)} {...props}>
{filteredChildren}
</div>
);
},
);
NavbarGeneric.displayName = "NavbarGeneric";
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
"use client";

import { GitcoinLogo } from "@/assets/logos/gitcoin";

const DefaultLogo = GitcoinLogo;
Expand Down
Loading
Loading