Add new sections and update navigation in components
This commit is contained in:
parent
d81f8eb208
commit
0cecec1424
7 changed files with 821 additions and 0 deletions
16
src/components/sections/misc/Authentication.astro
Normal file
16
src/components/sections/misc/Authentication.astro
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
---
|
||||||
|
// Import the necessary components from their respective component files
|
||||||
|
import LoginModal from "@components/ui/forms/LoginModal.astro";
|
||||||
|
import RegisterModal from "@components/ui/forms/RegisterModal.astro";
|
||||||
|
import RecoverModal from "@components/ui/forms/RecoverModal.astro";
|
||||||
|
import LoginBtn from "@components/ui/buttons/LoginBtn.astro";
|
||||||
|
---
|
||||||
|
|
||||||
|
<!-- Login Button -->
|
||||||
|
<LoginBtn />
|
||||||
|
<!-- Login Modal -->
|
||||||
|
<LoginModal />
|
||||||
|
<!-- Register Modal -->
|
||||||
|
<RegisterModal />
|
||||||
|
<!-- Password Recovery Modal -->
|
||||||
|
<RecoverModal />
|
||||||
118
src/components/sections/misc/ContactSection.astro
Normal file
118
src/components/sections/misc/ContactSection.astro
Normal file
|
|
@ -0,0 +1,118 @@
|
||||||
|
---
|
||||||
|
// Import the necessary dependencies.
|
||||||
|
import AuthBtn from "@components/ui/buttons/AuthBtn.astro";
|
||||||
|
import ContactIconBlock from "@components/ui/blocks/ContactIconBlock.astro";
|
||||||
|
import TextInput from "@components/ui/forms/input/TextInput.astro";
|
||||||
|
import EmailContactInput from "@components/ui/forms/input/EmailContactInput.astro";
|
||||||
|
import PhoneInput from "@components/ui/forms/input/PhoneInput.astro";
|
||||||
|
import TextAreaInput from "@components/ui/forms/input/TextAreaInput.astro";
|
||||||
|
import Icon from "@components/ui/icons/Icon.astro";
|
||||||
|
|
||||||
|
// Define the variables that will be used in this component
|
||||||
|
const title: string = "Contact us";
|
||||||
|
const subTitle: string =
|
||||||
|
"Have questions or want to discuss a project? Reach out, and let's craft the perfect solution with our tools and services.";
|
||||||
|
const formTitle: string = "Fill in the form below";
|
||||||
|
const formSubTitle: string = "We'll get back to you in 1-2 business days.";
|
||||||
|
---
|
||||||
|
|
||||||
|
<!-- Contact Us -->
|
||||||
|
<section class="mx-auto max-w-[85rem] px-4 py-10 sm:px-6 lg:px-8 lg:py-14">
|
||||||
|
<div class="mx-auto max-w-2xl lg:max-w-5xl">
|
||||||
|
<div class="text-center">
|
||||||
|
<h1
|
||||||
|
class="text-balance text-2xl font-bold tracking-tight text-neutral-800 dark:text-neutral-200 md:text-4xl md:leading-tight"
|
||||||
|
>
|
||||||
|
{title}
|
||||||
|
</h1>
|
||||||
|
<p class="mt-1 text-pretty text-neutral-600 dark:text-neutral-400">
|
||||||
|
{subTitle}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mt-12 grid items-center gap-6 lg:grid-cols-2 lg:gap-16">
|
||||||
|
<div class="flex flex-col rounded-xl p-4 sm:p-6 lg:p-8">
|
||||||
|
<h2
|
||||||
|
class="mb-8 text-xl font-bold text-neutral-700 dark:text-neutral-300"
|
||||||
|
>
|
||||||
|
{formTitle}
|
||||||
|
</h2>
|
||||||
|
<!-- Form for user input with various input fields.-->
|
||||||
|
<!-- Each field utilizes a different input component for the specific type of input (text, email, phone, and textarea)-->
|
||||||
|
<form>
|
||||||
|
<div class="grid gap-4">
|
||||||
|
<div class="grid grid-cols-1 gap-4 sm:grid-cols-2">
|
||||||
|
<TextInput
|
||||||
|
id="hs-firstname-contacts"
|
||||||
|
label="First Name"
|
||||||
|
name="hs-firstname-contacts"
|
||||||
|
/>
|
||||||
|
<TextInput
|
||||||
|
id="hs-lastname-contacts"
|
||||||
|
label="Last Name"
|
||||||
|
name="hs-firstname-contacts"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<EmailContactInput id="hs-email-contacts" />
|
||||||
|
<PhoneInput id="hs-phone-number" />
|
||||||
|
<TextAreaInput
|
||||||
|
id="hs-about-contacts"
|
||||||
|
label="Details"
|
||||||
|
name="hs-about-contacts"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mt-4 grid">
|
||||||
|
<AuthBtn title="Send Message" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mt-3 text-center">
|
||||||
|
<p class="text-sm text-neutral-600 dark:text-neutral-400">
|
||||||
|
{formSubTitle}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!--ContactIconBlocks are used to display different methods of contacting, including visiting office, email, browsing knowledgebase, and FAQ.-->
|
||||||
|
<div class="divide-y divide-neutral-300 dark:divide-neutral-700">
|
||||||
|
<ContactIconBlock
|
||||||
|
heading="Knowledgebase"
|
||||||
|
content="Browse through all of our knowledgebase articles."
|
||||||
|
isLinkVisible={true}
|
||||||
|
linkTitle="Visit guides & tutorials"
|
||||||
|
linkURL="#"
|
||||||
|
isArrowVisible={true}
|
||||||
|
><Icon name="question" />
|
||||||
|
</ContactIconBlock>
|
||||||
|
|
||||||
|
<ContactIconBlock
|
||||||
|
heading="FAQ"
|
||||||
|
content="Explore our FAQ for quick, clear answers to common queries."
|
||||||
|
isLinkVisible={true}
|
||||||
|
linkTitle="Visit FAQ"
|
||||||
|
linkURL="#"
|
||||||
|
isArrowVisible={true}
|
||||||
|
><Icon name="chatBubble" />
|
||||||
|
</ContactIconBlock>
|
||||||
|
|
||||||
|
<ContactIconBlock
|
||||||
|
heading="Visit our office"
|
||||||
|
content="UK ScrewFast"
|
||||||
|
isAddressVisible={true}
|
||||||
|
addressContent="72 Union Terrace, E10 4PE London"
|
||||||
|
><Icon name="mapPin" />
|
||||||
|
</ContactIconBlock>
|
||||||
|
|
||||||
|
<ContactIconBlock
|
||||||
|
heading="Contact us by email"
|
||||||
|
content="Prefer the written word? Drop us an email at"
|
||||||
|
isLinkVisible={true}
|
||||||
|
linkTitle="support@screwfast.uk"
|
||||||
|
linkURL="#"
|
||||||
|
><Icon name="envelopeOpen" />
|
||||||
|
</ContactIconBlock>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
79
src/components/sections/misc/FAQ.astro
Normal file
79
src/components/sections/misc/FAQ.astro
Normal file
|
|
@ -0,0 +1,79 @@
|
||||||
|
---
|
||||||
|
// Import the necessary AccordionItem component and JSON data
|
||||||
|
import AccordionItem from "@components/ui/blocks/AccordionItem.astro";
|
||||||
|
|
||||||
|
// Define props from Astro
|
||||||
|
const { title, faqs } = Astro.props;
|
||||||
|
|
||||||
|
// Define TypeScript interface for props
|
||||||
|
interface Faq {
|
||||||
|
question: string;
|
||||||
|
answer: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface FaqGroup {
|
||||||
|
subTitle?: string;
|
||||||
|
faqs: Faq[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
title: string;
|
||||||
|
faqs: FaqGroup;
|
||||||
|
}
|
||||||
|
// Define a helper function to generate ids dynamically.
|
||||||
|
const makeId = (base: any, index: any) => `${base}${index + 1}`;
|
||||||
|
---
|
||||||
|
|
||||||
|
<!-- Main container that holds all content. Customized for different viewport sizes. -->
|
||||||
|
<section
|
||||||
|
class="mx-auto max-w-[85rem] px-4 py-10 sm:px-6 lg:px-8 lg:py-14 2xl:max-w-full"
|
||||||
|
>
|
||||||
|
<div class="grid gap-10 md:grid-cols-5">
|
||||||
|
<div class="md:col-span-2">
|
||||||
|
<div class="max-w-xs">
|
||||||
|
<h2
|
||||||
|
class="text-2xl font-bold text-neutral-800 dark:text-neutral-200 md:text-4xl md:leading-tight"
|
||||||
|
>
|
||||||
|
<Fragment set:html={title} />
|
||||||
|
</h2>
|
||||||
|
<p class="mt-1 hidden text-neutral-600 dark:text-neutral-400 md:block">
|
||||||
|
{faqs.subTitle}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- FAQ accordion items -->
|
||||||
|
<div class="md:col-span-3">
|
||||||
|
<div
|
||||||
|
class="hs-accordion-group divide-y divide-neutral-200 dark:divide-neutral-700"
|
||||||
|
>
|
||||||
|
{
|
||||||
|
faqs.faqs.map((question, i) => {
|
||||||
|
// Generate ids dynamically for each FAQ accordion item.
|
||||||
|
let id = makeId(
|
||||||
|
"hs-basic-with-title-and-arrow-stretched-heading-",
|
||||||
|
i
|
||||||
|
);
|
||||||
|
let collapseId = makeId(
|
||||||
|
"hs-basic-with-title-and-arrow-stretched-collapse",
|
||||||
|
i
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<AccordionItem
|
||||||
|
{...question}
|
||||||
|
id={id}
|
||||||
|
collapseId={collapseId}
|
||||||
|
first={i === 0}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<!--Import the necessary Accordion plugin-->
|
||||||
|
<!--https://preline.co/plugins/html/accordion.html-->
|
||||||
|
<script>
|
||||||
|
import "@preline/accordion/index.js";
|
||||||
|
</script>
|
||||||
117
src/components/sections/navbar&footer/FooterSection.astro
Normal file
117
src/components/sections/navbar&footer/FooterSection.astro
Normal file
|
|
@ -0,0 +1,117 @@
|
||||||
|
---
|
||||||
|
// Import the necessary dependencies
|
||||||
|
import FooterSocialLink from "@components/ui/links/FooterSocialLink.astro";
|
||||||
|
import EmailFooterInput from "@components/ui/forms/input/EmailFooterInput.astro";
|
||||||
|
import enStrings from "@utils/navigation.ts";
|
||||||
|
import frStrings from "@utils/fr/navigation.ts";
|
||||||
|
import Icon from "@components/ui/icons/Icon.astro";
|
||||||
|
import BrandLogo from "@components/BrandLogo.astro";
|
||||||
|
import { SITE } from "@data/constants";
|
||||||
|
|
||||||
|
// Select the correct translation based on the page's lang prop:
|
||||||
|
const strings = Astro.currentLocale === "fr" ? frStrings : enStrings;
|
||||||
|
|
||||||
|
// Define the variables that will be used in this component
|
||||||
|
const sectionThreeTitle: string = Astro.currentLocale === "fr" ? "Rester à jour" : "Stay up to date";
|
||||||
|
const sectionThreeContent: string = Astro.currentLocale === "fr" ? "Restez informé des derniers outils et des offres exclusives." :
|
||||||
|
"Stay updated with the latest tools and exclusive deals.";
|
||||||
|
const crafted: string = Astro.currentLocale === "fr" ? "Fabriqué par" : "Crafted by";
|
||||||
|
---
|
||||||
|
|
||||||
|
<footer class="w-full bg-neutral-300 dark:bg-neutral-900">
|
||||||
|
<div
|
||||||
|
class="mx-auto w-full max-w-[85rem] px-4 py-10 sm:px-6 lg:px-16 lg:pt-20 2xl:max-w-screen-2xl"
|
||||||
|
>
|
||||||
|
<div class="grid grid-cols-2 gap-6 md:grid-cols-4 lg:grid-cols-5">
|
||||||
|
<div class="col-span-full lg:col-span-1">
|
||||||
|
<!-- Brand Logo -->
|
||||||
|
<BrandLogo class="h-auto w-32" />
|
||||||
|
</div>
|
||||||
|
<!-- An array of links for Product and Company sections -->
|
||||||
|
{
|
||||||
|
strings.footerLinks.map((section) => (
|
||||||
|
<div class="col-span-1">
|
||||||
|
<h3 class="font-bold text-neutral-800 dark:text-neutral-200">
|
||||||
|
{section.section}
|
||||||
|
</h3>
|
||||||
|
<ul class="mt-3 grid space-y-3">
|
||||||
|
{section.links.map((link, index) => (
|
||||||
|
<li>
|
||||||
|
<a
|
||||||
|
href={link.url}
|
||||||
|
class="inline-flex gap-x-2 rounded-lg text-neutral-600 outline-none ring-zinc-500 transition duration-300 hover:text-neutral-500 focus-visible:ring dark:text-neutral-400 dark:ring-zinc-200 dark:hover:text-neutral-300 dark:focus:outline-none"
|
||||||
|
>
|
||||||
|
{link.name}
|
||||||
|
</a>
|
||||||
|
{section.section === "Company" && index === 2 ? (
|
||||||
|
<span class="ms-1 inline rounded-lg bg-orange-500 px-2 py-1 text-xs font-bold text-neutral-50">
|
||||||
|
We're hiring!
|
||||||
|
</span>
|
||||||
|
) : null}
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
<div class="col-span-2">
|
||||||
|
<h3 class="font-bold text-neutral-800 dark:text-neutral-200">
|
||||||
|
{sectionThreeTitle}
|
||||||
|
</h3>
|
||||||
|
|
||||||
|
<form>
|
||||||
|
<EmailFooterInput />
|
||||||
|
<p class="mt-3 text-sm text-neutral-600 dark:text-neutral-400">
|
||||||
|
{sectionThreeContent}
|
||||||
|
</p>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="mt-9 grid gap-y-2 sm:mt-12 sm:flex sm:items-center sm:justify-between sm:gap-y-0"
|
||||||
|
>
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<p class="text-sm text-neutral-600 dark:text-neutral-400">
|
||||||
|
© <span id="current-year"></span> {SITE.title}. {crafted}
|
||||||
|
<a
|
||||||
|
class="rounded-lg font-medium underline underline-offset-2 outline-none ring-zinc-500 transition duration-300 hover:text-neutral-700 hover:decoration-dashed focus:outline-none focus-visible:ring dark:ring-zinc-200 dark:hover:text-neutral-300"
|
||||||
|
href="https://sobstvennoai.dev"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer">sobstvennoAI</a
|
||||||
|
>.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Social Brands -->
|
||||||
|
<div>
|
||||||
|
<FooterSocialLink url={strings.socialLinks.facebook}
|
||||||
|
><Icon name="facebookFooter" />
|
||||||
|
</FooterSocialLink>
|
||||||
|
|
||||||
|
<FooterSocialLink url={strings.socialLinks.x}
|
||||||
|
><Icon name="xFooter" /></FooterSocialLink
|
||||||
|
>
|
||||||
|
|
||||||
|
<FooterSocialLink url={strings.socialLinks.github}
|
||||||
|
><Icon name="githubFooter" />
|
||||||
|
</FooterSocialLink>
|
||||||
|
|
||||||
|
<FooterSocialLink url={strings.socialLinks.google}
|
||||||
|
><Icon name="googleFooter" />
|
||||||
|
</FooterSocialLink>
|
||||||
|
|
||||||
|
<FooterSocialLink url={strings.socialLinks.slack}
|
||||||
|
><Icon name="slackFooter" />
|
||||||
|
</FooterSocialLink>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const year = new Date().getFullYear();
|
||||||
|
const element = document.getElementById("current-year");
|
||||||
|
element!.innerText = year.toString();
|
||||||
|
</script>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
214
src/components/sections/navbar&footer/Navbar.astro
Normal file
214
src/components/sections/navbar&footer/Navbar.astro
Normal file
|
|
@ -0,0 +1,214 @@
|
||||||
|
---
|
||||||
|
//Import relevant dependencies
|
||||||
|
import ThemeIcon from "@components/ThemeIcon.astro";
|
||||||
|
import NavLink from "@components/ui/links/NavLink.astro";
|
||||||
|
import Authentication from "../misc/Authentication.astro";
|
||||||
|
import enStrings from "@utils/navigation.ts";
|
||||||
|
import frStrings from "@utils/fr/navigation.ts";
|
||||||
|
import BrandLogo from "@components/BrandLogo.astro";
|
||||||
|
import LanguagePicker from "@components/ui/LanguagePicker.astro";
|
||||||
|
|
||||||
|
// Select the correct translation based on the page's lang prop:
|
||||||
|
const strings = Astro.currentLocale === "fr" ? frStrings : enStrings;
|
||||||
|
const homeUrl = Astro.currentLocale === "fr" ? "/fr" : "/";
|
||||||
|
---
|
||||||
|
|
||||||
|
<!-- Main header component -->
|
||||||
|
<header
|
||||||
|
class="sticky inset-x-0 top-4 z-50 flex w-full flex-wrap text-sm md:flex-nowrap md:justify-start"
|
||||||
|
>
|
||||||
|
<!-- Navigation container -->
|
||||||
|
<nav
|
||||||
|
class="relative mx-2 w-full rounded-[36px] border border-yellow-100/40 bg-yellow-50/60 px-4 py-3 backdrop-blur-md dark:border-neutral-700/40 dark:bg-neutral-800/80 dark:backdrop-blur-md md:flex md:items-center md:justify-between md:px-6 md:py-0 lg:px-8 xl:mx-auto"
|
||||||
|
aria-label="Global"
|
||||||
|
>
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<!-- Brand logo -->
|
||||||
|
<a
|
||||||
|
class="flex-none rounded-lg text-xl font-bold outline-none ring-zinc-500 focus-visible:ring dark:ring-zinc-200 dark:focus:outline-none"
|
||||||
|
href={homeUrl}
|
||||||
|
aria-label="Brand"
|
||||||
|
>
|
||||||
|
<BrandLogo class="h-auto w-24" />
|
||||||
|
</a>
|
||||||
|
<!-- Collapse toggle for smaller screens -->
|
||||||
|
<div class="ml-auto mr-5 md:hidden">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="hs-collapse-toggle flex h-8 w-8 items-center justify-center rounded-full text-sm font-bold text-neutral-600 transition duration-300 hover:bg-neutral-200 disabled:pointer-events-none disabled:opacity-50 dark:text-neutral-400 dark:hover:bg-neutral-700 dark:focus:outline-none"
|
||||||
|
data-hs-collapse="#navbar-collapse-with-animation"
|
||||||
|
aria-controls="navbar-collapse-with-animation"
|
||||||
|
aria-label="Toggle navigation"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
class="h-[1.25rem] w-[1.25rem] flex-shrink-0 hs-collapse-open:hidden"
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-width="2"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
>
|
||||||
|
<line x1="3" x2="21" y1="6" y2="6"></line>
|
||||||
|
<line x1="3" x2="21" y1="12" y2="12"></line>
|
||||||
|
<line x1="3" x2="21" y1="18" y2="18"></line>
|
||||||
|
</svg>
|
||||||
|
<svg
|
||||||
|
class="hidden h-[1.25rem] w-[1.25rem] flex-shrink-0 hs-collapse-open:block"
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-width="2"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
>
|
||||||
|
<path d="M18 6 6 18"></path>
|
||||||
|
<path d="m6 6 12 12"></path>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<!-- ThemeIcon component specifically for smaller screens -->
|
||||||
|
<span class="inline-block md:hidden">
|
||||||
|
<ThemeIcon />
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<!-- Contains navigation links -->
|
||||||
|
<div
|
||||||
|
id="navbar-collapse-with-animation"
|
||||||
|
class="hs-collapse hidden grow basis-full overflow-hidden transition-all duration-300 md:block"
|
||||||
|
>
|
||||||
|
<!-- Navigation links container -->
|
||||||
|
<div
|
||||||
|
class="mt-5 flex flex-col gap-x-0 gap-y-4 md:mt-0 md:flex-row md:items-center md:justify-end md:gap-x-4 lg:gap-x-7 md:gap-y-0 md:ps-7"
|
||||||
|
>
|
||||||
|
<!-- Navigation links and Authentication component -->
|
||||||
|
{strings.navBarLinks.map(link => (
|
||||||
|
<NavLink url={link.url} name={link.name} />
|
||||||
|
))}
|
||||||
|
|
||||||
|
<Authentication />
|
||||||
|
<LanguagePicker />
|
||||||
|
<!-- ThemeIcon component specifically for larger screens -->
|
||||||
|
<span class="hidden md:inline-block">
|
||||||
|
<ThemeIcon />
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
<!-- Theme Appearance script to manage light/dark modes -->
|
||||||
|
<script is:inline>
|
||||||
|
const HSThemeAppearance = {
|
||||||
|
init() {
|
||||||
|
const defaultTheme = "default";
|
||||||
|
let theme = localStorage.getItem("hs_theme") || defaultTheme;
|
||||||
|
|
||||||
|
if (document.querySelector("html").classList.contains("dark")) return;
|
||||||
|
this.setAppearance(theme);
|
||||||
|
},
|
||||||
|
_resetStylesOnLoad() {
|
||||||
|
const $resetStyles = document.createElement("style");
|
||||||
|
$resetStyles.innerText = `*{transition: unset !important;}`;
|
||||||
|
$resetStyles.setAttribute("data-hs-appearance-onload-styles", "");
|
||||||
|
document.head.appendChild($resetStyles);
|
||||||
|
return $resetStyles;
|
||||||
|
},
|
||||||
|
setAppearance(theme, saveInStore = true, dispatchEvent = true) {
|
||||||
|
const $resetStylesEl = this._resetStylesOnLoad();
|
||||||
|
|
||||||
|
if (saveInStore) {
|
||||||
|
localStorage.setItem("hs_theme", theme);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (theme === "auto") {
|
||||||
|
theme = window.matchMedia("(prefers-color-scheme: dark)").matches
|
||||||
|
? "dark"
|
||||||
|
: "default";
|
||||||
|
}
|
||||||
|
|
||||||
|
document.querySelector("html").classList.remove("dark");
|
||||||
|
document.querySelector("html").classList.remove("default");
|
||||||
|
document.querySelector("html").classList.remove("auto");
|
||||||
|
|
||||||
|
document
|
||||||
|
.querySelector("html")
|
||||||
|
.classList.add(this.getOriginalAppearance());
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
$resetStylesEl.remove();
|
||||||
|
});
|
||||||
|
|
||||||
|
if (dispatchEvent) {
|
||||||
|
window.dispatchEvent(
|
||||||
|
new CustomEvent("on-hs-appearance-change", { detail: theme }),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getAppearance() {
|
||||||
|
let theme = this.getOriginalAppearance();
|
||||||
|
if (theme === "auto") {
|
||||||
|
theme = window.matchMedia("(prefers-color-scheme: dark)").matches
|
||||||
|
? "dark"
|
||||||
|
: "default";
|
||||||
|
}
|
||||||
|
return theme;
|
||||||
|
},
|
||||||
|
getOriginalAppearance() {
|
||||||
|
const defaultTheme = "default";
|
||||||
|
return localStorage.getItem("hs_theme") || defaultTheme;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
HSThemeAppearance.init();
|
||||||
|
|
||||||
|
window
|
||||||
|
.matchMedia("(prefers-color-scheme: dark)")
|
||||||
|
.addEventListener("change", () => {
|
||||||
|
if (HSThemeAppearance.getOriginalAppearance() === "auto") {
|
||||||
|
HSThemeAppearance.setAppearance("auto", false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
window.addEventListener("load", () => {
|
||||||
|
const $clickableThemes = document.querySelectorAll(
|
||||||
|
"[data-hs-theme-click-value]",
|
||||||
|
);
|
||||||
|
const $switchableThemes = document.querySelectorAll(
|
||||||
|
"[data-hs-theme-switch]",
|
||||||
|
);
|
||||||
|
|
||||||
|
$clickableThemes.forEach(($item) => {
|
||||||
|
$item.addEventListener("click", () =>
|
||||||
|
HSThemeAppearance.setAppearance(
|
||||||
|
$item.getAttribute("data-hs-theme-click-value"),
|
||||||
|
true,
|
||||||
|
$item,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
$switchableThemes.forEach(($item) => {
|
||||||
|
$item.addEventListener("change", (e) => {
|
||||||
|
HSThemeAppearance.setAppearance(e.target.checked ? "dark" : "default");
|
||||||
|
});
|
||||||
|
|
||||||
|
$item.checked = HSThemeAppearance.getAppearance() === "dark";
|
||||||
|
});
|
||||||
|
|
||||||
|
window.addEventListener("on-hs-appearance-change", (e) => {
|
||||||
|
$switchableThemes.forEach(($item) => {
|
||||||
|
$item.checked = e.detail === "dark";
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<!--Import the necessary Collapse and Overlay plugins-->
|
||||||
|
<!--https://preline.co/plugins/html/collapse.html-->
|
||||||
|
<!--https://preline.co/plugins/html/overlay.html-->
|
||||||
|
<script>
|
||||||
|
import "@preline/collapse/index.js";
|
||||||
|
import "@preline/overlay/index.js";
|
||||||
|
</script>
|
||||||
223
src/components/sections/navbar&footer/NavbarMegaMenu.astro
Normal file
223
src/components/sections/navbar&footer/NavbarMegaMenu.astro
Normal file
|
|
@ -0,0 +1,223 @@
|
||||||
|
---
|
||||||
|
//Import relevant dependencies
|
||||||
|
import ThemeIcon from "@components/ThemeIcon.astro";
|
||||||
|
import NavLink from "@components/ui/links/NavLink.astro";
|
||||||
|
import MegaMenuLink from "@components/ui/links/MegaMenuLink.astro";
|
||||||
|
import Authentication from "../misc/Authentication.astro";
|
||||||
|
import enStrings from "@utils/navigation.ts";
|
||||||
|
import frStrings from "@utils/fr/navigation.ts";
|
||||||
|
import BrandLogo from "@components/BrandLogo.astro";
|
||||||
|
import LanguagePicker from "@components/ui/LanguagePicker.astro";
|
||||||
|
|
||||||
|
// Select the correct translation based on the page's lang prop:
|
||||||
|
const strings = Astro.currentLocale === "fr" ? frStrings : enStrings;
|
||||||
|
const homeUrl = Astro.currentLocale === "fr" ? "/fr" : "/";
|
||||||
|
---
|
||||||
|
|
||||||
|
<!-- Main header component -->
|
||||||
|
<header
|
||||||
|
class="sticky inset-x-0 top-4 z-50 flex w-full flex-wrap text-sm md:flex-nowrap md:justify-start"
|
||||||
|
>
|
||||||
|
<!-- Navigation container -->
|
||||||
|
<nav
|
||||||
|
class="relative mx-2 w-full rounded-[36px] border border-yellow-100/40 bg-yellow-50/60 px-4 py-3 backdrop-blur-md dark:border-neutral-700/40 dark:bg-neutral-800/80 dark:backdrop-blur-md md:flex md:items-center md:justify-between md:px-6 md:py-0 lg:px-8 xl:mx-auto"
|
||||||
|
aria-label="Global"
|
||||||
|
>
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<!-- Brand logo -->
|
||||||
|
<a
|
||||||
|
class="flex-none rounded-lg text-xl font-bold outline-none ring-zinc-500 focus-visible:ring dark:ring-zinc-200 dark:focus:outline-none"
|
||||||
|
href={homeUrl}
|
||||||
|
aria-label="Brand"
|
||||||
|
>
|
||||||
|
<BrandLogo class="h-auto w-24" />
|
||||||
|
</a>
|
||||||
|
<!-- Collapse toggle for smaller screens -->
|
||||||
|
<div class="ml-auto mr-5 md:hidden">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="hs-collapse-toggle flex h-8 w-8 items-center justify-center rounded-full text-sm font-bold text-neutral-600 transition duration-300 hover:bg-neutral-200 disabled:pointer-events-none disabled:opacity-50 dark:text-neutral-400 dark:hover:bg-neutral-700 dark:focus:outline-none"
|
||||||
|
data-hs-collapse="#navbar-collapse-with-animation"
|
||||||
|
aria-controls="navbar-collapse-with-animation"
|
||||||
|
aria-label="Toggle navigation"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
class="h-[1.25rem] w-[1.25rem] flex-shrink-0 hs-collapse-open:hidden"
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-width="2"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
>
|
||||||
|
<line x1="3" x2="21" y1="6" y2="6"></line>
|
||||||
|
<line x1="3" x2="21" y1="12" y2="12"></line>
|
||||||
|
<line x1="3" x2="21" y1="18" y2="18"></line>
|
||||||
|
</svg>
|
||||||
|
<svg
|
||||||
|
class="hidden h-[1.25rem] w-[1.25rem] flex-shrink-0 hs-collapse-open:block"
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-width="2"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
>
|
||||||
|
<path d="M18 6 6 18"></path>
|
||||||
|
<path d="m6 6 12 12"></path>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<!-- ThemeIcon component specifically for smaller screens -->
|
||||||
|
<span class="inline-block md:hidden">
|
||||||
|
<ThemeIcon />
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<!-- Contains navigation links -->
|
||||||
|
<div
|
||||||
|
id="navbar-collapse-with-animation"
|
||||||
|
class="hs-collapse hidden grow basis-full overflow-hidden transition-all duration-300 md:block"
|
||||||
|
>
|
||||||
|
<!-- Navigation links container -->
|
||||||
|
<div
|
||||||
|
class="mt-5 flex flex-col gap-x-0 gap-y-4 md:mt-0 md:flex-row md:items-center md:justify-end md:gap-x-4 md:gap-y-0 md:ps-7 lg:gap-x-7"
|
||||||
|
>
|
||||||
|
<!-- Navigation links and Authentication component -->
|
||||||
|
{
|
||||||
|
strings.navBarLinks.map((link) => {
|
||||||
|
if (link.name === "Services") {
|
||||||
|
return <MegaMenuLink />;
|
||||||
|
} else {
|
||||||
|
return <NavLink url={link.url} name={link.name} />;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
<Authentication />
|
||||||
|
<LanguagePicker />
|
||||||
|
<!-- ThemeIcon component specifically for larger screens -->
|
||||||
|
<span class="hidden md:inline-block">
|
||||||
|
<ThemeIcon />
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
<!-- Theme Appearance script to manage light/dark modes -->
|
||||||
|
<script is:inline>
|
||||||
|
const HSThemeAppearance = {
|
||||||
|
init() {
|
||||||
|
const defaultTheme = "default";
|
||||||
|
let theme = localStorage.getItem("hs_theme") || defaultTheme;
|
||||||
|
|
||||||
|
if (document.querySelector("html").classList.contains("dark")) return;
|
||||||
|
this.setAppearance(theme);
|
||||||
|
},
|
||||||
|
_resetStylesOnLoad() {
|
||||||
|
const $resetStyles = document.createElement("style");
|
||||||
|
$resetStyles.innerText = `*{transition: unset !important;}`;
|
||||||
|
$resetStyles.setAttribute("data-hs-appearance-onload-styles", "");
|
||||||
|
document.head.appendChild($resetStyles);
|
||||||
|
return $resetStyles;
|
||||||
|
},
|
||||||
|
setAppearance(theme, saveInStore = true, dispatchEvent = true) {
|
||||||
|
const $resetStylesEl = this._resetStylesOnLoad();
|
||||||
|
|
||||||
|
if (saveInStore) {
|
||||||
|
localStorage.setItem("hs_theme", theme);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (theme === "auto") {
|
||||||
|
theme = window.matchMedia("(prefers-color-scheme: dark)").matches
|
||||||
|
? "dark"
|
||||||
|
: "default";
|
||||||
|
}
|
||||||
|
|
||||||
|
document.querySelector("html").classList.remove("dark");
|
||||||
|
document.querySelector("html").classList.remove("default");
|
||||||
|
document.querySelector("html").classList.remove("auto");
|
||||||
|
|
||||||
|
document
|
||||||
|
.querySelector("html")
|
||||||
|
.classList.add(this.getOriginalAppearance());
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
$resetStylesEl.remove();
|
||||||
|
});
|
||||||
|
|
||||||
|
if (dispatchEvent) {
|
||||||
|
window.dispatchEvent(
|
||||||
|
new CustomEvent("on-hs-appearance-change", { detail: theme })
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getAppearance() {
|
||||||
|
let theme = this.getOriginalAppearance();
|
||||||
|
if (theme === "auto") {
|
||||||
|
theme = window.matchMedia("(prefers-color-scheme: dark)").matches
|
||||||
|
? "dark"
|
||||||
|
: "default";
|
||||||
|
}
|
||||||
|
return theme;
|
||||||
|
},
|
||||||
|
getOriginalAppearance() {
|
||||||
|
const defaultTheme = "default";
|
||||||
|
return localStorage.getItem("hs_theme") || defaultTheme;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
HSThemeAppearance.init();
|
||||||
|
|
||||||
|
window
|
||||||
|
.matchMedia("(prefers-color-scheme: dark)")
|
||||||
|
.addEventListener("change", () => {
|
||||||
|
if (HSThemeAppearance.getOriginalAppearance() === "auto") {
|
||||||
|
HSThemeAppearance.setAppearance("auto", false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
window.addEventListener("load", () => {
|
||||||
|
const $clickableThemes = document.querySelectorAll(
|
||||||
|
"[data-hs-theme-click-value]"
|
||||||
|
);
|
||||||
|
const $switchableThemes = document.querySelectorAll(
|
||||||
|
"[data-hs-theme-switch]"
|
||||||
|
);
|
||||||
|
|
||||||
|
$clickableThemes.forEach(($item) => {
|
||||||
|
$item.addEventListener("click", () =>
|
||||||
|
HSThemeAppearance.setAppearance(
|
||||||
|
$item.getAttribute("data-hs-theme-click-value"),
|
||||||
|
true,
|
||||||
|
$item
|
||||||
|
)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
$switchableThemes.forEach(($item) => {
|
||||||
|
$item.addEventListener("change", (e) => {
|
||||||
|
HSThemeAppearance.setAppearance(e.target.checked ? "dark" : "default");
|
||||||
|
});
|
||||||
|
|
||||||
|
$item.checked = HSThemeAppearance.getAppearance() === "dark";
|
||||||
|
});
|
||||||
|
|
||||||
|
window.addEventListener("on-hs-appearance-change", (e) => {
|
||||||
|
$switchableThemes.forEach(($item) => {
|
||||||
|
$item.checked = e.detail === "dark";
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<!--Import the necessary Collapse and Overlay plugins-->
|
||||||
|
<!--https://preline.co/plugins/html/collapse.html-->
|
||||||
|
<!--https://preline.co/plugins/html/overlay.html-->
|
||||||
|
<!--https://preline.co/plugins/html/dropdown.html-->
|
||||||
|
<script>
|
||||||
|
import "@preline/collapse/index.js";
|
||||||
|
import "@preline/overlay/index.js";
|
||||||
|
import "@preline/dropdown/index.js";
|
||||||
|
</script>
|
||||||
54
src/data_files/mega_link.ts
Normal file
54
src/data_files/mega_link.ts
Normal file
|
|
@ -0,0 +1,54 @@
|
||||||
|
// Define data structures for services and success stories
|
||||||
|
export const servicesData = [
|
||||||
|
{
|
||||||
|
title: "Explore Advice and Explanations",
|
||||||
|
description:
|
||||||
|
"Dive deep into helpful guides and explanations for all of ScrewFast's features",
|
||||||
|
icon: "guides",
|
||||||
|
url: "#",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Discover Integrations",
|
||||||
|
description:
|
||||||
|
"Supercharge Your Workflow. Seamless integrations with all your favorite tools",
|
||||||
|
icon: "puzzle",
|
||||||
|
url: "#",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Expert Services",
|
||||||
|
description: "Go beyond tools with ScrewFast's expert services",
|
||||||
|
icon: "rocket",
|
||||||
|
url: "#",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Cutting-Edge Tools",
|
||||||
|
description:
|
||||||
|
"Build Smarter, Faster. Experience next-level efficiency with ScrewFast's cutting-edge construction tools",
|
||||||
|
icon: "hammer",
|
||||||
|
url: "#",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Simple Plans",
|
||||||
|
description:
|
||||||
|
"Boost your efficiency with ScrewFast's straightforward, value-driven plans",
|
||||||
|
icon: "sparks",
|
||||||
|
url: "#",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Community Forum",
|
||||||
|
description: "Learn, share, and connect with other ScrewFast users",
|
||||||
|
icon: "community",
|
||||||
|
url: "#",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export const successStoriesData = [
|
||||||
|
{
|
||||||
|
image:
|
||||||
|
"https://images.unsplash.com/photo-1544005313-94ddf0286df2?q=80&w=1376&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D8&auto=format&fit=facearea&facepad=2&w=320&h=320&q=80",
|
||||||
|
alt: "Image Description",
|
||||||
|
description:
|
||||||
|
"See how ScrewFast has empowered businesses of all sizes to achieve outstanding results.",
|
||||||
|
learnMoreUrl: "#",
|
||||||
|
},
|
||||||
|
];
|
||||||
Loading…
Add table
Reference in a new issue