Skip to content

Commit

Permalink
Added navbar and CodeBlock for fast scripts
Browse files Browse the repository at this point in the history
  • Loading branch information
angeldevildev committed Sep 17, 2024
1 parent 6f5243e commit 5f8a8dd
Show file tree
Hide file tree
Showing 12 changed files with 537 additions and 29 deletions.
151 changes: 151 additions & 0 deletions app/Components/Sections/Navbar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
'use client'

import { useState, useEffect, useRef } from 'react'
import { Menu, Search, X, Sun, Moon } from 'lucide-react'
import Link from 'next/link'
import { useTheme } from '../ThemeContext'

export default function Navbar() {
const [isSearchOpen, setIsSearchOpen] = useState(false)
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false)
const mobileMenuRef = useRef<HTMLDivElement>(null)
const { theme, toggleTheme } = useTheme()

const toggleSearch = () => setIsSearchOpen(!isSearchOpen)
const toggleMobileMenu = () => setIsMobileMenuOpen(!isMobileMenuOpen)

useEffect(() => {
const handleOutsideClick = (event: MouseEvent) => {
if (mobileMenuRef.current && !mobileMenuRef.current.contains(event.target as Node)) {
setIsMobileMenuOpen(false)
}
}

document.addEventListener('mousedown', handleOutsideClick)
return () => {
document.removeEventListener('mousedown', handleOutsideClick)
}
}, [])

const navItems = [
{ name: 'Home', href: '/' },
{ name: 'Products', href: '/products' },
{ name: 'About', href: '/about' },
{ name: 'Contact', href: '/contact' },
]

return (
<nav className="bg-white dark:bg-gray-800 shadow-sm rounded-xl transition-colors duration-200">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="flex items-center justify-between h-16">
<div className="flex items-center">
<Link href="/" className="flex-shrink-0" prefetch={false}>
<svg className="h-8 w-8 text-blue-600 dark:text-blue-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M13 10V3L4 14h7v7l9-11h-7z" />
</svg>
</Link>
<div className="hidden md:block ml-10">
<div className="flex items-baseline space-x-4">
{navItems.map((item) => (
<Link
key={item.name}
href={item.href}
className="text-gray-600 dark:text-gray-300 hover:text-blue-600 dark:hover:text-blue-400 px-3 py-2 rounded-md text-sm font-medium transition-colors no-underline"
prefetch={false}
>
{item.name}
</Link>
))}
</div>
</div>
</div>
<div className="hidden md:flex items-center space-x-4">
<div className="relative flex items-center">
<input
type="search"
placeholder="Search..."
className={`px-3 py-2 pr-10 rounded-md text-sm bg-gray-100 dark:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-blue-500 transition-all duration-300 ease-in-out ${isSearchOpen ? 'w-64 opacity-100' : 'w-0 opacity-0'}`}
/>
<button
onClick={toggleSearch}
className="p-2 rounded-md text-gray-600 dark:text-gray-300 hover:text-blue-600 dark:hover:text-blue-400 focus:outline-none focus:ring-2 focus:ring-blue-500 absolute right-0"
aria-label="Search"
>
<Search className="h-5 w-5" />
</button>
</div>
<button
onClick={toggleTheme}
className="p-2 rounded-md text-gray-600 dark:text-gray-300 hover:text-blue-600 dark:hover:text-blue-400 focus:outline-none focus:ring-2 focus:ring-blue-500"
aria-label={`Switch to ${theme === 'light' ? 'dark' : 'light'} mode`}
>
{theme === 'light' ? <Moon className="h-5 w-5" /> : <Sun className="h-5 w-5" />}
</button>
</div>
<div className="md:hidden">
<button
onClick={toggleMobileMenu}
className="p-2 rounded-md text-gray-600 dark:text-gray-300 hover:text-blue-600 dark:hover:text-blue-400 focus:outline-none focus:ring-2 focus:ring-blue-500"
aria-label="Open menu"
>
<Menu className="h-5 w-5" />
</button>
</div>
</div>
</div>

{/* Mobile menu */}
<div
ref={mobileMenuRef}
className={`fixed inset-y-0 right-0 w-64 bg-white dark:bg-gray-800 shadow-lg transform transition-transform duration-300 ease-in-out ${isMobileMenuOpen ? 'translate-x-0' : 'translate-x-full'} z-50`}
>
<div className="p-6">
<div className="flex items-center justify-between mb-8">
<Link href="/" className="flex-shrink-0" prefetch={false}>
<svg className="h-8 w-8 text-blue-600 dark:text-blue-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M13 10V3L4 14h7v7l9-11h-7z" />
</svg>
</Link>
<button
onClick={toggleMobileMenu}
className="p-2 rounded-md text-gray-600 dark:text-gray-300 hover:text-blue-600 dark:hover:text-blue-400 focus:outline-none focus:ring-2 focus:ring-blue-500"
aria-label="Close menu"
>
<X className="h-5 w-5" />
</button>
</div>
<nav className="space-y-1">
{navItems.map((item) => (
<Link
key={item.name}
href={item.href}
className="block px-3 py-2 rounded-md text-base font-medium text-gray-700 dark:text-gray-300 hover:text-blue-600 dark:hover:text-blue-400 hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors no-underline"
prefetch={false}
>
{item.name}
</Link>
))}
</nav>
<div className="mt-6 space-y-4">
<div className="relative">
<input
type="search"
placeholder="Search..."
className="w-full px-3 py-2 rounded-md text-sm bg-gray-100 dark:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-blue-500"
/>
<Search className="absolute right-3 top-2.5 h-4 w-4 text-gray-400 dark:text-gray-500" />
</div>
<button
onClick={toggleTheme}
className="w-full p-2 rounded-md text-gray-600 dark:text-gray-300 hover:text-blue-600 dark:hover:text-blue-400 focus:outline-none focus:ring-2 focus:ring-blue-500 flex items-center justify-center"
aria-label={`Switch to ${theme === 'light' ? 'dark' : 'light'} mode`}
>
{theme === 'light' ? <Moon className="h-5 w-5 mr-2" /> : <Sun className="h-5 w-5 mr-2" />}
{theme === 'light' ? 'Dark Mode' : 'Light Mode'}
</button>
</div>
</div>
</div>
</nav>
)
}
2 changes: 2 additions & 0 deletions app/Components/StyledContainer.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
// This instead should be used for every component

"use client";

import React, { useState, ReactNode } from "react";
Expand Down
46 changes: 46 additions & 0 deletions app/Components/ThemeContext.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
'use client'

import React, { createContext, useContext, useState, useEffect } from 'react'

type Theme = 'light' | 'dark'

interface ThemeContextType {
theme: Theme
toggleTheme: () => void
}

const ThemeContext = createContext<ThemeContextType | undefined>(undefined)

export const ThemeProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
const [theme, setTheme] = useState<Theme>('dark')

useEffect(() => {
const savedTheme = localStorage.getItem('theme') as Theme | null
if (savedTheme) {
setTheme(savedTheme)
}
}, [])

useEffect(() => {
document.documentElement.classList.toggle('dark', theme === 'dark')
localStorage.setItem('theme', theme)
}, [theme])

const toggleTheme = () => {
setTheme(prevTheme => prevTheme === 'light' ? 'dark' : 'light')
}

return (
<ThemeContext.Provider value={{ theme, toggleTheme }}>
{children}
</ThemeContext.Provider>
)
}

export const useTheme = () => {
const context = useContext(ThemeContext)
if (context === undefined) {
throw new Error('useTheme must be used within a ThemeProvider')
}
return context
}
79 changes: 79 additions & 0 deletions app/DocsComponents/CodeBlock.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
'use client'

import React, { useState } from 'react'
import { Copy, Check } from 'lucide-react'
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'
import { vscDarkPlus } from 'react-syntax-highlighter/dist/esm/styles/prism'

interface CodeBlockProps {
code: string
fileName: string
fileExtension: string
className?: string
}

export default function CodeBlock({
code,
fileName = 'file',
fileExtension = 'tsx',
className,
}: CodeBlockProps) {
const [copied, setCopied] = useState(false)

const handleCopy = () => {
navigator.clipboard.writeText(code).then(() => {
setCopied(true)
setTimeout(() => setCopied(false), 2000)
})
}

// Create a custom style by modifying vscDarkPlus
const customStyle = {
...vscDarkPlus,
'pre[class*="language-"]': {
...vscDarkPlus['pre[class*="language-"]'],
background: '#1e1e1e',
padding: '16px',
margin: 0,
},
'code[class*="language-"]': {
...vscDarkPlus['code[class*="language-"]'],
background: 'none',
textShadow: 'none',
},
}

return (
<div className={`bg-[#1e1e1e] text-foreground rounded-md w-full max-w-3xl ${className}`}>
<div className="flex justify-between items-center px-4 py-2 bg-[#252526] rounded-t-md">
<div className="flex items-center space-x-2">
<span className="flex items-center space-x-1 text-gray-300">
<span className="bg-[#007acc] text-white text-xs font-bold px-2 py-1 rounded-xl">
{fileExtension.toUpperCase()}
</span>
<div className='p-1'>
<span><code>{fileName}.{fileExtension}</code></span>
</div>
</span>
</div>
<button className="text-gray-400 hover:text-white" onClick={handleCopy}>
{copied ? <Check size={18} /> : <Copy size={18} />}
</button>
</div>

<div className="bg-[#1e1e1e] rounded-b-md overflow-hidden">
<SyntaxHighlighter
language={fileExtension}
style={customStyle}
customStyle={{
borderRadius: '0 0 8px 8px',
}}
showLineNumbers={false}
wrapLines={true}
>
{code}
</SyntaxHighlighter>
</div>
</div>
)
}
1 change: 1 addition & 0 deletions app/docs/[[...slug]]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type { Metadata } from "next";
import { DocsPage, DocsBody } from "next-docs-ui/page";
import { notFound } from "next/navigation";


export default async function Page({
params,
}: {
Expand Down
5 changes: 4 additions & 1 deletion app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { RootProvider } from "next-docs-ui/provider";
import { Inter } from "next/font/google";
import type { ReactNode } from "react";
import { Metadata } from "next";
import { ThemeProvider } from "./Components/ThemeContext";

const inter = Inter({
subsets: ["latin"],
Expand Down Expand Up @@ -32,7 +33,9 @@ export default function Layout({ children }: { children: ReactNode }) {
return (
<html lang="en" className={inter.className}>
<body>
<RootProvider>{children}</RootProvider>
<ThemeProvider>
<RootProvider>{children}</RootProvider>
</ThemeProvider>
</body>
</html>
);
Expand Down
Loading

0 comments on commit 5f8a8dd

Please sign in to comment.