chore: Improve website UI and add .vscode to .gitignore

- Update website footer to improve responsiveness and layout.
- Improve the styling and layout of the Home page components.
- Update the Layout component to handle mobile sidebar better.
- Improve Markdown rendering for better responsiveness.
- Remove unused NavBar component.
- Remove svelte-vscode extension from extensions.json.
- Add .vscode folder to .gitignore to prevent accidental commits.
This commit is contained in:
Mahmoud Emad 2025-05-11 17:43:11 +03:00
parent e68d0bbe8c
commit 486b5ceb05
10 changed files with 257 additions and 141 deletions

1
.gitignore vendored
View File

@ -23,3 +23,4 @@ dist-ssr
*.sln *.sln
*.sw? *.sw?
*.pnpm-store *.pnpm-store
*.vscode

View File

@ -1,3 +0,0 @@
{
"recommendations": ["svelte.svelte-vscode"]
}

View File

@ -2,40 +2,49 @@
// Footer component // Footer component
</script> </script>
<footer class="bg-gray-100 border-t border-gray-200 py-6 px-4 w-full mt-auto"> <footer
class="bg-gray-100 border-t border-gray-200 py-4 sm:py-6 px-3 sm:px-4 w-full mt-auto"
>
<div class="container mx-auto max-w-6xl"> <div class="container mx-auto max-w-6xl">
<div class="flex flex-col md:flex-row justify-between items-center"> <div
<div class="mb-4 md:mb-0"> class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-6 md:gap-4"
>
<!-- Logo and tagline -->
<div class="text-center sm:text-left">
<div class="text-lg font-semibold text-blue-800">SecureWeb</div> <div class="text-lg font-semibold text-blue-800">SecureWeb</div>
<p class="text-gray-600 text-sm mt-1"> <p class="text-gray-600 text-sm mt-1">
Secure and reliable web solutions Secure and reliable web solutions
</p> </p>
</div> </div>
<div class="flex space-x-6"> <!-- Navigation links -->
<div
class="flex flex-wrap justify-center sm:justify-start gap-x-6 gap-y-2"
>
<a <a
href="/" href="/"
class="text-gray-600 hover:text-blue-800 transition-colors" class="text-gray-600 hover:text-blue-800 transition-colors text-sm sm:text-base"
>Home</a >Home</a
> >
<a <a
href="/about" href="/about"
class="text-gray-600 hover:text-blue-800 transition-colors" class="text-gray-600 hover:text-blue-800 transition-colors text-sm sm:text-base"
>About</a >About</a
> >
<a <a
href="/services" href="/services"
class="text-gray-600 hover:text-blue-800 transition-colors" class="text-gray-600 hover:text-blue-800 transition-colors text-sm sm:text-base"
>Services</a >Services</a
> >
<a <a
href="/contact" href="/contact"
class="text-gray-600 hover:text-blue-800 transition-colors" class="text-gray-600 hover:text-blue-800 transition-colors text-sm sm:text-base"
>Contact</a >Contact</a
> >
</div> </div>
<div class="flex space-x-4 mt-4 md:mt-0"> <!-- Social media icons -->
<div class="flex justify-center md:justify-end space-x-4">
<a <a
href="/twitter" href="/twitter"
aria-label="Twitter" aria-label="Twitter"
@ -88,7 +97,7 @@
</div> </div>
<div <div
class="border-t border-gray-200 mt-6 pt-6 text-center text-gray-500 text-sm" class="border-t border-gray-200 mt-4 sm:mt-6 pt-4 sm:pt-6 text-center text-gray-500 text-xs sm:text-sm"
> >
<p> <p>
&copy; {new Date().getFullYear()} SecureWeb. All rights reserved. &copy; {new Date().getFullYear()} SecureWeb. All rights reserved.

View File

@ -8,34 +8,38 @@
$: actualPath = contentPath || "introduction/introduction"; $: actualPath = contentPath || "introduction/introduction";
</script> </script>
<div class="py-8 px-4 max-w-7xl mx-auto"> <div class="py-4 sm:py-6 md:py-8 px-3 sm:px-4 max-w-7xl mx-auto">
{#if contentPath} {#if contentPath}
<div class="bg-white rounded-lg shadow-sm p-6 mb-8"> <div
class="bg-white rounded-lg shadow-sm p-4 sm:p-6 mb-6 sm:mb-8 overflow-x-auto"
>
<MarkdownContent path={actualPath} /> <MarkdownContent path={actualPath} />
</div> </div>
{:else} {:else}
<!-- Hero Section --> <!-- Hero Section -->
<section class="mb-20 text-center md:text-left"> <section class="mb-12 sm:mb-16 md:mb-20 text-center md:text-left">
<div class="md:flex md:items-center md:justify-between"> <div class="md:flex md:items-center md:justify-between">
<div class="md:w-1/2 mb-10 md:mb-0"> <div class="md:w-1/2 mb-8 md:mb-0 md:pr-6">
<h1 <h1
class="text-4xl md:text-5xl font-bold mb-6 text-gray-900 leading-tight" class="text-3xl sm:text-4xl md:text-5xl font-bold mb-4 sm:mb-6 text-gray-900 leading-tight"
> >
Welcome to <span class="text-blue-600">SecureWeb</span> Welcome to <span class="text-blue-600">SecureWeb</span>
</h1> </h1>
<p <p
class="text-xl text-gray-600 max-w-2xl mb-8 leading-relaxed" class="text-lg sm:text-xl text-gray-600 max-w-2xl mb-6 sm:mb-8 leading-relaxed"
> >
A comprehensive platform for secure and reliable web A comprehensive platform for secure and reliable web
solutions designed for modern businesses. solutions designed for modern businesses.
</p> </p>
<div class="flex flex-col sm:flex-row gap-4"> <div
class="flex flex-col xs:flex-row justify-center md:justify-start gap-3 sm:gap-4"
>
<button <button
class="bg-blue-600 hover:bg-blue-700 text-white px-8 py-3 rounded-md font-medium transition-colors duration-200" class="bg-blue-600 hover:bg-blue-700 text-white px-6 sm:px-8 py-2.5 sm:py-3 rounded-md font-medium transition-colors duration-200 w-full xs:w-auto"
>Get Started</button >Get Started</button
> >
<button <button
class="bg-gray-100 hover:bg-gray-200 text-gray-800 px-8 py-3 rounded-md font-medium border border-gray-300 transition-colors duration-200" class="bg-gray-100 hover:bg-gray-200 text-gray-800 px-6 sm:px-8 py-2.5 sm:py-3 rounded-md font-medium border border-gray-300 transition-colors duration-200 w-full xs:w-auto"
>Learn More</button >Learn More</button
> >
</div> </div>
@ -45,70 +49,89 @@
src="https://via.placeholder.com/600x400/f3f4f6/1e40af?text=SecureWeb" src="https://via.placeholder.com/600x400/f3f4f6/1e40af?text=SecureWeb"
alt="SecureWeb Platform" alt="SecureWeb Platform"
class="rounded-lg shadow-lg w-full" class="rounded-lg shadow-lg w-full"
loading="lazy"
/> />
</div> </div>
</div> </div>
</section> </section>
<!-- Features Section --> <!-- Features Section -->
<section class="mb-20"> <section class="mb-12 sm:mb-16 md:mb-20">
<div class="text-center mb-12"> <div class="text-center mb-8 sm:mb-12">
<h2 class="text-3xl font-bold mb-4 text-gray-900"> <h2
class="text-2xl sm:text-3xl font-bold mb-3 sm:mb-4 text-gray-900"
>
Our Features Our Features
</h2> </h2>
<p class="text-xl text-gray-600 max-w-3xl mx-auto"> <p class="text-lg sm:text-xl text-gray-600 max-w-3xl mx-auto">
Discover what makes SecureWeb the preferred choice for Discover what makes SecureWeb the preferred choice for
security-conscious businesses. security-conscious businesses.
</p> </p>
</div> </div>
<div class="grid grid-cols-1 md:grid-cols-3 gap-10">
<div <div
class="bg-gray-50 p-8 rounded-xl shadow-md hover:shadow-lg transition-shadow border border-gray-100" class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-6 sm:gap-8 md:gap-10"
> >
<div <div
class="bg-blue-100 p-3 rounded-full w-14 h-14 flex items-center justify-center mb-6" class="bg-gray-50 p-6 sm:p-8 rounded-xl shadow-md hover:shadow-lg transition-shadow border border-gray-100"
> >
<Shield class="h-7 w-7 text-blue-600" /> <div
class="bg-blue-100 p-3 rounded-full w-12 h-12 sm:w-14 sm:h-14 flex items-center justify-center mb-4 sm:mb-6"
>
<Shield class="h-6 w-6 sm:h-7 sm:w-7 text-blue-600" />
</div> </div>
<h3 class="text-xl font-bold mb-4 text-gray-800"> <h3
class="text-lg sm:text-xl font-bold mb-3 sm:mb-4 text-gray-800"
>
Advanced Security Advanced Security
</h3> </h3>
<p class="text-gray-600 leading-relaxed"> <p
class="text-gray-600 leading-relaxed text-sm sm:text-base"
>
State-of-the-art encryption and security protocols to State-of-the-art encryption and security protocols to
keep your data protected from threats. keep your data protected from threats.
</p> </p>
</div> </div>
<div <div
class="bg-white p-8 rounded-xl shadow-md hover:shadow-lg transition-shadow border border-gray-100" class="bg-white p-6 sm:p-8 rounded-xl shadow-md hover:shadow-lg transition-shadow border border-gray-100"
> >
<div <div
class="bg-blue-100 p-3 rounded-full w-14 h-14 flex items-center justify-center mb-6" class="bg-blue-100 p-3 rounded-full w-12 h-12 sm:w-14 sm:h-14 flex items-center justify-center mb-4 sm:mb-6"
> >
<Zap class="h-7 w-7 text-blue-600" /> <Zap class="h-6 w-6 sm:h-7 sm:w-7 text-blue-600" />
</div> </div>
<h3 class="text-xl font-bold mb-4 text-gray-800"> <h3
class="text-lg sm:text-xl font-bold mb-3 sm:mb-4 text-gray-800"
>
Lightning Performance Lightning Performance
</h3> </h3>
<p class="text-gray-600 leading-relaxed"> <p
class="text-gray-600 leading-relaxed text-sm sm:text-base"
>
Optimized for speed and efficiency, ensuring a smooth Optimized for speed and efficiency, ensuring a smooth
and responsive user experience. and responsive user experience.
</p> </p>
</div> </div>
<div <div
class="bg-gray-50 p-8 rounded-xl shadow-md hover:shadow-lg transition-shadow border border-gray-100" class="bg-gray-50 p-6 sm:p-8 rounded-xl shadow-md hover:shadow-lg transition-shadow border border-gray-100 sm:col-span-2 md:col-span-1"
> >
<div <div
class="bg-blue-100 p-3 rounded-full w-14 h-14 flex items-center justify-center mb-6" class="bg-blue-100 p-3 rounded-full w-12 h-12 sm:w-14 sm:h-14 flex items-center justify-center mb-4 sm:mb-6"
> >
<Smartphone class="h-7 w-7 text-blue-600" /> <Smartphone
class="h-6 w-6 sm:h-7 sm:w-7 text-blue-600"
/>
</div> </div>
<h3 class="text-xl font-bold mb-4 text-gray-800"> <h3
class="text-lg sm:text-xl font-bold mb-3 sm:mb-4 text-gray-800"
>
Responsive Design Responsive Design
</h3> </h3>
<p class="text-gray-600 leading-relaxed"> <p
class="text-gray-600 leading-relaxed text-sm sm:text-base"
>
Fully responsive layouts that provide a seamless Fully responsive layouts that provide a seamless
experience across all devices and screen sizes. experience across all devices and screen sizes.
</p> </p>
@ -119,22 +142,24 @@
<!-- CTA Section --> <!-- CTA Section -->
<section> <section>
<div <div
class="bg-gradient-to-r from-blue-600 to-blue-800 rounded-2xl p-10 text-white shadow-xl" class="bg-gradient-to-r from-blue-600 to-blue-800 rounded-xl sm:rounded-2xl p-6 sm:p-8 md:p-10 text-white shadow-lg sm:shadow-xl"
> >
<div class="md:flex md:items-center md:justify-between"> <div class="md:flex md:items-center md:justify-between">
<div class="mb-8 md:mb-0 md:w-2/3"> <div class="mb-6 md:mb-0 md:w-2/3 md:pr-6">
<h2 class="text-3xl font-bold mb-4"> <h2 class="text-2xl sm:text-3xl font-bold mb-3 sm:mb-4">
Ready to Get Started? Ready to Get Started?
</h2> </h2>
<p class="text-lg text-blue-100 mb-0 max-w-2xl"> <p
class="text-base sm:text-lg text-blue-100 mb-0 max-w-2xl"
>
Join thousands of satisfied users who trust Join thousands of satisfied users who trust
SecureWeb for their web security needs. Sign up SecureWeb for their web security needs. Sign up
today and experience the difference. today and experience the difference.
</p> </p>
</div> </div>
<div> <div class="flex justify-center md:justify-end">
<button <button
class="bg-white text-blue-700 hover:bg-blue-50 px-8 py-3 rounded-lg font-semibold text-lg shadow-md transition-colors" class="bg-white text-blue-700 hover:bg-blue-50 px-6 sm:px-8 py-2.5 sm:py-3 rounded-lg font-semibold text-base sm:text-lg shadow-md transition-colors w-full xs:w-auto"
> >
Sign Up Now Sign Up Now
</button> </button>

View File

@ -12,20 +12,36 @@
function toggleSidebar() { function toggleSidebar() {
sidebarVisible = !sidebarVisible; sidebarVisible = !sidebarVisible;
// On mobile, when sidebar is opened, add a class to prevent body scrolling
if (isMobile && sidebarVisible) {
document.body.classList.add("overflow-hidden");
} else {
document.body.classList.remove("overflow-hidden");
}
} }
function handleNavItemClick(path: string) { function handleNavItemClick(path: string) {
selectedContentPath = path; selectedContentPath = path;
// Auto-close sidebar on mobile after navigation
if (isMobile) {
sidebarVisible = false;
document.body.classList.remove("overflow-hidden");
}
} }
onMount(() => { onMount(() => {
const checkMobile = () => { const checkMobile = () => {
const wasMobile = isMobile;
isMobile = window.innerWidth < 768; isMobile = window.innerWidth < 768;
// Only change sidebar visibility when transitioning between mobile and desktop
if (wasMobile !== isMobile) {
if (isMobile) { if (isMobile) {
sidebarVisible = false; sidebarVisible = false;
} else { } else {
sidebarVisible = true; sidebarVisible = true;
} }
}
}; };
checkMobile(); checkMobile();
@ -38,19 +54,34 @@
</script> </script>
<div class="flex flex-col min-h-screen bg-gray-50"> <div class="flex flex-col min-h-screen bg-gray-50">
<Navbar {toggleSidebar} /> <Navbar {toggleSidebar} {isMobile} {sidebarVisible} />
<div class="flex flex-1 pt-16"> <div class="flex flex-1 pt-16 relative">
<NavDataProvider let:navData> <!-- Overlay for mobile sidebar -->
{#if sidebarVisible} {#if sidebarVisible && isMobile}
<Sidebar {navData} onNavItemClick={handleNavItemClick} /> <div
class="fixed inset-0 bg-gray-900 bg-opacity-50 z-20 transition-opacity duration-300"
on:click={toggleSidebar}
aria-hidden="true"
></div>
{/if} {/if}
<NavDataProvider let:navData>
<!-- Sidebar with improved mobile handling -->
<Sidebar
{navData}
onNavItemClick={handleNavItemClick}
visible={sidebarVisible}
{isMobile}
/>
<main <main
class={`flex-1 transition-all duration-300 ${sidebarVisible ? "md:ml-64" : "ml-0"}`} class={`flex-1 transition-all duration-300 ${
sidebarVisible && !isMobile ? "md:ml-64" : "ml-0"
}`}
> >
<div <div
class="container mx-auto px-4 py-8 min-h-[calc(100vh-12rem)]" class="container mx-auto px-3 sm:px-4 py-4 sm:py-8 min-h-[calc(100vh-12rem)]"
> >
{#if selectedContentPath} {#if selectedContentPath}
<Home contentPath={selectedContentPath} /> <Home contentPath={selectedContentPath} />

View File

@ -94,41 +94,57 @@
<style> <style>
.markdown-content { .markdown-content {
padding: 1rem; padding: 0.5rem;
max-width: 100%; max-width: 100%;
} }
@media (min-width: 640px) {
.markdown-content {
padding: 1rem;
}
}
.loading, .loading,
.error { .error {
padding: 2rem; padding: 1rem;
text-align: center; text-align: center;
} }
@media (min-width: 640px) {
.loading,
.error {
padding: 2rem;
}
}
.error { .error {
color: #e53e3e; color: #e53e3e;
} }
.content :global(h1) { .content :global(h1) {
font-size: 2.5rem; font-size: 1.75rem;
font-weight: 700; font-weight: 700;
margin-bottom: 1.5rem; margin-bottom: 1rem;
color: #1e40af; color: #1e40af;
word-break: break-word;
} }
.content :global(h2) { .content :global(h2) {
font-size: 2rem;
font-weight: 600;
margin-top: 2rem;
margin-bottom: 1rem;
color: #1e3a8a;
}
.content :global(h3) {
font-size: 1.5rem; font-size: 1.5rem;
font-weight: 600; font-weight: 600;
margin-top: 1.5rem; margin-top: 1.5rem;
margin-bottom: 0.75rem; margin-bottom: 0.75rem;
color: #1e3a8a; color: #1e3a8a;
word-break: break-word;
}
.content :global(h3) {
font-size: 1.25rem;
font-weight: 600;
margin-top: 1.25rem;
margin-bottom: 0.5rem;
color: #1e3a8a;
word-break: break-word;
} }
.content :global(p) { .content :global(p) {
@ -139,7 +155,7 @@
.content :global(ul), .content :global(ul),
.content :global(ol) { .content :global(ol) {
margin-bottom: 1rem; margin-bottom: 1rem;
margin-left: 2rem; margin-left: 1.5rem;
} }
.content :global(li) { .content :global(li) {
@ -149,6 +165,7 @@
.content :global(a) { .content :global(a) {
color: #2563eb; color: #2563eb;
text-decoration: none; text-decoration: none;
word-break: break-word;
} }
.content :global(a:hover) { .content :global(a:hover) {
@ -169,11 +186,14 @@
padding: 0.2rem 0.4rem; padding: 0.2rem 0.4rem;
border-radius: 0.25rem; border-radius: 0.25rem;
font-family: monospace; font-family: monospace;
font-size: 0.875em;
word-break: break-word;
white-space: pre-wrap;
} }
.content :global(pre) { .content :global(pre) {
background-color: #f3f4f6; background-color: #f3f4f6;
padding: 1rem; padding: 0.75rem;
border-radius: 0.5rem; border-radius: 0.5rem;
overflow-x: auto; overflow-x: auto;
margin-bottom: 1rem; margin-bottom: 1rem;
@ -182,6 +202,8 @@
.content :global(pre code) { .content :global(pre code) {
background-color: transparent; background-color: transparent;
padding: 0; padding: 0;
white-space: pre;
word-break: normal;
} }
.content :global(img) { .content :global(img) {
@ -189,27 +211,59 @@
height: auto; height: auto;
border-radius: 0.5rem; border-radius: 0.5rem;
margin: 1rem 0; margin: 1rem 0;
display: block;
} }
.content :global(hr) { .content :global(hr) {
border: 0; border: 0;
border-top: 1px solid #e5e7eb; border-top: 1px solid #e5e7eb;
margin: 2rem 0; margin: 1.5rem 0;
} }
.content :global(table) { .content :global(table) {
width: 100%; width: 100%;
border-collapse: collapse; border-collapse: collapse;
margin-bottom: 1rem; margin-bottom: 1rem;
display: block;
overflow-x: auto;
} }
.content :global(th), .content :global(th),
.content :global(td) { .content :global(td) {
border: 1px solid #e5e7eb; border: 1px solid #e5e7eb;
padding: 0.5rem; padding: 0.5rem;
min-width: 100px;
} }
.content :global(th) { .content :global(th) {
background-color: #f9fafb; background-color: #f9fafb;
} }
@media (min-width: 640px) {
.content :global(h1) {
font-size: 2.25rem;
margin-bottom: 1.5rem;
}
.content :global(h2) {
font-size: 1.75rem;
margin-top: 2rem;
margin-bottom: 1rem;
}
.content :global(h3) {
font-size: 1.5rem;
margin-top: 1.5rem;
margin-bottom: 0.75rem;
}
.content :global(ul),
.content :global(ol) {
margin-left: 2rem;
}
.content :global(pre) {
padding: 1rem;
}
}
</style> </style>

View File

@ -1,49 +0,0 @@
<script lang="ts">
import type { NavItem } from '../types/nav';
import * as Accordion from '$lib/components/ui/accordion'; // Assuming Shadcn Accordion is in $lib/components/ui/accordion
import { Button } from '$lib/components/ui/button'; // Assuming Shadcn Button is in $lib/components/ui/button
import { ChevronDown } from 'lucide-svelte'; // Assuming lucide-svelte is installed for icons
export let navData: NavItem[];
function renderNavItem(item: NavItem): any {
if (item.children && item.children.length > 0) {
return Accordion.Item;
} else {
return 'a'; // Render as a link
}
}
</script>
<nav class="w-64 bg-gray-800 text-white h-full">
<div class="p-4">
<h2 class="text-xl font-semibold mb-4">OurWorld</h2>
</div>
<Accordion.Root class="w-full">
{#each navData as item}
{#if item.children && item.children.length > 0}
<Accordion.Item value={item.label}>
<Accordion.Trigger class="flex justify-between items-center w-full text-left p-4 hover:bg-gray-700">
{item.label}
<ChevronDown class="h-4 w-4 transition-transform duration-200" />
</Accordion.Trigger>
<Accordion.Content>
<ul class="pl-4">
{#each item.children as child}
<li>
<a href={child.link} class="block p-2 hover:bg-gray-700">
{child.label}
</a>
</li>
{/each}
</ul>
</Accordion.Content>
</Accordion.Item>
{:else}
<a href={item.link} class="block p-4 hover:bg-gray-700">
{item.label}
</a>
{/if}
{/each}
</Accordion.Root>
</nav>

View File

@ -1,24 +1,38 @@
<script lang="ts"> <script lang="ts">
import { Menu, Search, User, Bell } from "lucide-svelte"; import { Menu, Search, User, Bell, X } from "lucide-svelte";
export let toggleSidebar: () => void = () => {}; export let toggleSidebar: () => void = () => {};
export let isMobile: boolean = false;
export let sidebarVisible: boolean = false;
</script> </script>
<header <header
class="bg-gray-50 border-b border-gray-200 fixed top-0 left-0 right-0 z-10 h-16 flex items-center justify-between px-4 shadow-sm" class="bg-gray-50 border-b border-gray-200 fixed top-0 left-0 right-0 z-30 h-16 flex items-center justify-between px-3 sm:px-4 shadow-sm"
> >
<div class="flex items-center"> <div class="flex items-center">
<button <button
class="md:hidden mr-3 p-2 rounded-md hover:bg-gray-100 text-gray-700" class="mr-3 p-2 rounded-md hover:bg-gray-100 text-gray-700"
on:click={toggleSidebar} on:click={toggleSidebar}
aria-label="Toggle sidebar" aria-label={sidebarVisible ? "Close sidebar" : "Open sidebar"}
> >
{#if isMobile && sidebarVisible}
<X class="h-5 w-5" />
{:else}
<Menu class="h-5 w-5" /> <Menu class="h-5 w-5" />
{/if}
</button> </button>
<div class="text-xl font-bold text-blue-800">SecureWeb</div> <div class="text-lg sm:text-xl font-bold text-blue-800">SecureWeb</div>
</div> </div>
<div class="flex items-center space-x-4"> <div class="flex items-center space-x-2 sm:space-x-4">
<!-- Search button for mobile -->
<button
class="md:hidden p-2 rounded-md hover:bg-gray-100 text-gray-700"
>
<Search class="h-5 w-5" />
</button>
<!-- Search bar for desktop -->
<div class="relative hidden md:block"> <div class="relative hidden md:block">
<Search <Search
class="h-4 w-4 absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400" class="h-4 w-4 absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400"
@ -26,12 +40,12 @@
<input <input
type="text" type="text"
placeholder="Search..." placeholder="Search..."
class="pl-9 pr-4 py-1.5 text-sm rounded-md border border-gray-300 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent w-48 lg:w-64" class="pl-9 pr-4 py-1.5 text-sm rounded-md border border-gray-300 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent w-40 lg:w-64"
/> />
</div> </div>
<button <button
class="p-2 rounded-full hover:bg-gray-100 text-gray-700 relative" class="p-2 rounded-full hover:bg-gray-100 text-gray-700 relative hidden sm:block"
> >
<Bell class="h-5 w-5" /> <Bell class="h-5 w-5" />
<span <span

View File

@ -1,11 +1,12 @@
<script lang="ts"> <script lang="ts">
import type { NavItem } from "../types/nav"; import type { NavItem } from "../types/nav";
import { ChevronDown } from "lucide-svelte"; import { ChevronDown, X } from "lucide-svelte";
import { onMount } from "svelte";
export let navData: NavItem[] = []; export let navData: NavItem[] = [];
// Define a prop for the event handler
export let onNavItemClick: (path: string) => void = () => {}; export let onNavItemClick: (path: string) => void = () => {};
export let visible: boolean = true;
export let isMobile: boolean = false;
// Track which sections are expanded // Track which sections are expanded
let expanded: Record<string, boolean> = {}; let expanded: Record<string, boolean> = {};
@ -13,6 +14,21 @@
// Track active item // Track active item
let activePath: string = ""; let activePath: string = "";
// Auto-expand the section containing the active item
$: if (activePath) {
// Find which section contains this path
navData.forEach((item) => {
if (item.children) {
const hasActivePath = item.children.some(
(child) => child.link === activePath,
);
if (hasActivePath) {
expanded[item.label] = true;
}
}
});
}
function toggleSection(label: string) { function toggleSection(label: string) {
expanded[label] = !expanded[label]; expanded[label] = !expanded[label];
} }
@ -28,22 +44,32 @@
// Call the event handler prop // Call the event handler prop
onNavItemClick(docPath); onNavItemClick(docPath);
} }
// Initialize with first section expanded
onMount(() => {
if (navData.length > 0) {
expanded[navData[0].label] = true;
}
});
</script> </script>
<aside <aside
class="sidebar w-64 bg-gray-50 border-r border-gray-200 h-screen fixed top-0 left-0 pt-16 overflow-y-auto shadow-sm" class="sidebar bg-gray-50 border-r border-gray-200 h-screen fixed top-0 left-0 pt-16 overflow-y-auto shadow-sm z-20
{isMobile ? 'w-[85%] max-w-xs' : 'w-64'}
transition-transform duration-300 ease-in-out
{visible ? 'translate-x-0' : '-translate-x-full'}"
> >
<nav class="w-full"> <nav class="w-full">
{#each navData as item} {#each navData as item}
{#if item.children && item.children.length > 0} {#if item.children && item.children.length > 0}
<div class="border-b border-gray-200"> <div class="border-b border-gray-200">
<button <button
class="flex justify-between items-center w-full text-left p-4 hover:bg-gray-100 text-gray-700 font-medium" class="flex justify-between items-center w-full text-left p-3 sm:p-4 hover:bg-gray-100 text-gray-700 font-medium"
on:click={() => toggleSection(item.label)} on:click={() => toggleSection(item.label)}
> >
{item.label} <span class="truncate">{item.label}</span>
<ChevronDown <ChevronDown
class="h-4 w-4 transition-transform duration-200 {expanded[ class="h-4 w-4 flex-shrink-0 transition-transform duration-200 {expanded[
item.label item.label
] ]
? 'rotate-180' ? 'rotate-180'
@ -56,7 +82,7 @@
{#each item.children as child} {#each item.children as child}
<a <a
href={child.link} href={child.link}
class="block p-3 pl-8 hover:bg-gray-100 text-gray-600 border-t border-gray-100 {activePath === class="block p-3 pl-6 sm:pl-8 hover:bg-gray-100 text-gray-600 border-t border-gray-100 truncate {activePath ===
child.link child.link
? 'bg-blue-50 text-blue-700 font-medium' ? 'bg-blue-50 text-blue-700 font-medium'
: ''}" : ''}"
@ -72,7 +98,7 @@
{:else} {:else}
<a <a
href={item.link} href={item.link}
class="block p-4 hover:bg-gray-100 text-gray-700 border-b border-gray-200 {activePath === class="block p-3 sm:p-4 hover:bg-gray-100 text-gray-700 border-b border-gray-200 truncate {activePath ===
item.link item.link
? 'bg-blue-50 text-blue-700 font-medium' ? 'bg-blue-50 text-blue-700 font-medium'
: ''}" : ''}"

View File

@ -4,6 +4,14 @@ export default {
'./src/**/*.{html,js,svelte,ts}', './src/**/*.{html,js,svelte,ts}',
], ],
theme: { theme: {
screens: {
'xs': '480px',
'sm': '640px',
'md': '768px',
'lg': '1024px',
'xl': '1280px',
'2xl': '1536px',
},
extend: { extend: {
colors: { colors: {
blue: { blue: {