Refactor components and update animation scripts

This commit is contained in:
Emil Gulamov 2024-06-29 03:38:42 +04:00
parent 0cecec1424
commit b867fbc93f
14 changed files with 216 additions and 897 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -1,16 +0,0 @@
---
// 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 />

View file

@ -1,118 +0,0 @@
---
// 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>

View file

@ -1,79 +0,0 @@
---
// 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>

View file

@ -1,117 +0,0 @@
---
// 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>

View file

@ -1,214 +0,0 @@
---
//Import relevant dependencies
import ThemeIcon from "@components/ThemeIcon.astro";
import NavLink from "@components/ui/links/NavLink.astro";
import Authentication from "./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>

View file

@ -1,223 +0,0 @@
---
//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 "./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>

View file

@ -417,8 +417,105 @@ export const Icons = {
d: "m20.893 13.393-1.135-1.135a2.252 2.252 0 0 1-.421-.585l-1.08-2.16a.414.414 0 0 0-.663-.107.827.827 0 0 1-.812.21l-1.273-.363a.89.89 0 0 0-.738 1.595l.587.39c.59.395.674 1.23.172 1.732l-.2.2c-.212.212-.33.498-.33.796v.41c0 .409-.11.809-.32 1.158l-1.315 2.191a2.11 2.11 0 0 1-1.81 1.025 1.055 1.055 0 0 1-1.055-1.055v-1.172c0-.92-.56-1.747-1.414-2.089l-.655-.261a2.25 2.25 0 0 1-1.383-2.46l.007-.042a2.25 2.25 0 0 1 .29-.787l.09-.15a2.25 2.25 0 0 1 2.37-1.048l1.178.236a1.125 1.125 0 0 0 1.302-.795l.208-.73a1.125 1.125 0 0 0-.578-1.315l-.665-.332-.091.091a2.25 2.25 0 0 1-1.591.659h-.18c-.249 0-.487.1-.662.274a.931.931 0 0 1-1.458-1.137l1.411-2.353a2.25 2.25 0 0 0 .286-.76m11.928 9.869A9 9 0 0 0 8.965 3.525m11.928 9.868A9 9 0 1 1 8.965 3.525", d: "m20.893 13.393-1.135-1.135a2.252 2.252 0 0 1-.421-.585l-1.08-2.16a.414.414 0 0 0-.663-.107.827.827 0 0 1-.812.21l-1.273-.363a.89.89 0 0 0-.738 1.595l.587.39c.59.395.674 1.23.172 1.732l-.2.2c-.212.212-.33.498-.33.796v.41c0 .409-.11.809-.32 1.158l-1.315 2.191a2.11 2.11 0 0 1-1.81 1.025 1.055 1.055 0 0 1-1.055-1.055v-1.172c0-.92-.56-1.747-1.414-2.089l-.655-.261a2.25 2.25 0 0 1-1.383-2.46l.007-.042a2.25 2.25 0 0 1 .29-.787l.09-.15a2.25 2.25 0 0 1 2.37-1.048l1.178.236a1.125 1.125 0 0 0 1.302-.795l.208-.73a1.125 1.125 0 0 0-.578-1.315l-.665-.332-.091.091a2.25 2.25 0 0 1-1.591.659h-.18c-.249 0-.487.1-.662.274a.931.931 0 0 1-1.458-1.137l1.411-2.353a2.25 2.25 0 0 0 .286-.76m11.928 9.869A9 9 0 0 0 8.965 3.525m11.928 9.868A9 9 0 1 1 8.965 3.525",
}, },
], ],
class: class: "w-4 h-4 flex-shrink-0",
"w-4 h-4 flex-shrink-0", viewBox: "0 0 24 24",
fill: "none",
strokeWidth: "1.5",
strokeLinecap: "round",
strokeLinejoin: "round",
stroke: "currentColor",
},
guides: {
paths: [
{
d: "M12 6.042A8.967 8.967 0 0 0 6 3.75c-1.052 0-2.062.18-3 .512v14.25A8.987 8.987 0 0 1 6 18c2.305 0 4.408.867 6 2.292m0-14.25a8.966 8.966 0 0 1 6-2.292c1.052 0 2.062.18 3 .512v14.25A8.987 8.987 0 0 0 18 18a8.967 8.967 0 0 0-6 2.292m0-14.25v14.25",
},
],
class: "mt-1 size-5 flex-shrink-0",
viewBox: "0 0 24 24",
fill: "none",
strokeWidth: "1.5",
strokeLinecap: "round",
strokeLinejoin: "round",
stroke: "currentColor",
},
puzzle: {
paths: [
{
d: "M2.25 7.125C2.25 6.504 2.754 6 3.375 6h6c.621 0 1.125.504 1.125 1.125v3.75c0 .621-.504 1.125-1.125 1.125h-6a1.125 1.125 0 0 1-1.125-1.125v-3.75ZM14.25 8.625c0-.621.504-1.125 1.125-1.125h5.25c.621 0 1.125.504 1.125 1.125v8.25c0 .621-.504 1.125-1.125 1.125h-5.25a1.125 1.125 0 0 1-1.125-1.125v-8.25ZM3.75 16.125c0-.621.504-1.125 1.125-1.125h5.25c.621 0 1.125.504 1.125 1.125v2.25c0 .621-.504 1.125-1.125 1.125h-5.25a1.125 1.125 0 0 1-1.125-1.125v-2.25Z",
},
],
class: "mt-1 size-5 flex-shrink-0",
viewBox: "0 0 24 24",
fill: "none",
strokeWidth: "1.5",
strokeLinecap: "round",
strokeLinejoin: "round",
stroke: "currentColor",
},
rocket: {
paths: [
{
d: "M15.59 14.37a6 6 0 0 1-5.84 7.38v-4.8m5.84-2.58a14.98 14.98 0 0 0 6.16-12.12A14.98 14.98 0 0 0 9.631 8.41m5.96 5.96a14.926 14.926 0 0 1-5.841 2.58m-.119-8.54a6 6 0 0 0-7.381 5.84h4.8m2.581-5.84a14.927 14.927 0 0 0-2.58 5.84m2.699 2.7c-.103.021-.207.041-.311.06a15.09 15.09 0 0 1-2.448-2.448 14.9 14.9 0 0 1 .06-.312m-2.24 2.39a4.493 4.493 0 0 0-1.757 4.306 4.493 4.493 0 0 0 4.306-1.758M16.5 9a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0Z",
},
],
class: "mt-1 size-5 flex-shrink-0",
viewBox: "0 0 24 24",
fill: "none",
strokeWidth: "1.5",
strokeLinecap: "round",
strokeLinejoin: "round",
stroke: "currentColor",
},
hammer: {
paths: [
{
d: "M11.42 15.17 17.25 21A2.652 2.652 0 0 0 21 17.25l-5.877-5.877M11.42 15.17l2.496-3.03c.317-.384.74-.626 1.208-.766M11.42 15.17l-4.655 5.653a2.548 2.548 0 1 1-3.586-3.586l6.837-5.63m5.108-.233c.55-.164 1.163-.188 1.743-.14a4.5 4.5 0 0 0 4.486-6.336l-3.276 3.277a3.004 3.004 0 0 1-2.25-2.25l3.276-3.276a4.5 4.5 0 0 0-6.336 4.486c.091 1.076-.071 2.264-.904 2.95l-.102.085m-1.745 1.437L5.909 7.5H4.5L2.25 3.75l1.5-1.5L7.5 4.5v1.409l4.26 4.26m-1.745 1.437 1.745-1.437m6.615 8.206L15.75 15.75M4.867 19.125h.008v.008h-.008v-.008Z",
},
],
class: "mt-1 size-5 flex-shrink-0",
viewBox: "0 0 24 24",
fill: "none",
strokeWidth: "1.5",
strokeLinecap: "round",
strokeLinejoin: "round",
stroke: "currentColor",
},
sparks: {
paths: [
{
d: "M9.813 15.904 9 18.75l-.813-2.846a4.5 4.5 0 0 0-3.09-3.09L2.25 12l2.846-.813a4.5 4.5 0 0 0 3.09-3.09L9 5.25l.813 2.846a4.5 4.5 0 0 0 3.09 3.09L15.75 12l-2.846.813a4.5 4.5 0 0 0-3.09 3.09ZM18.259 8.715 18 9.75l-.259-1.035a3.375 3.375 0 0 0-2.455-2.456L14.25 6l1.036-.259a3.375 3.375 0 0 0 2.455-2.456L18 2.25l.259 1.035a3.375 3.375 0 0 0 2.456 2.456L21.75 6l-1.035.259a3.375 3.375 0 0 0-2.456 2.456ZM16.894 20.567 16.5 21.75l-.394-1.183a2.25 2.25 0 0 0-1.423-1.423L13.5 18.75l1.183-.394a2.25 2.25 0 0 0 1.423-1.423l.394-1.183.394 1.183a2.25 2.25 0 0 0 1.423 1.423l1.183.394-1.183.394a2.25 2.25 0 0 0-1.423 1.423Z",
},
],
class: "mt-1 size-5 flex-shrink-0",
viewBox: "0 0 24 24",
fill: "none",
strokeWidth: "1.5",
strokeLinecap: "round",
strokeLinejoin: "round",
stroke: "currentColor",
},
community: {
paths: [
{
d: "M15 19.128a9.38 9.38 0 0 0 2.625.372 9.337 9.337 0 0 0 4.121-.952 4.125 4.125 0 0 0-7.533-2.493M15 19.128v-.003c0-1.113-.285-2.16-.786-3.07M15 19.128v.106A12.318 12.318 0 0 1 8.624 21c-2.331 0-4.512-.645-6.374-1.766l-.001-.109a6.375 6.375 0 0 1 11.964-3.07M12 6.375a3.375 3.375 0 1 1-6.75 0 3.375 3.375 0 0 1 6.75 0Zm8.25 2.25a2.625 2.625 0 1 1-5.25 0 2.625 2.625 0 0 1 5.25 0Z",
},
],
class: "mt-1 size-5 flex-shrink-0",
viewBox: "0 0 24 24",
fill: "none",
strokeWidth: "1.5",
strokeLinecap: "round",
strokeLinejoin: "round",
stroke: "currentColor",
},
chevronDown: {
paths: [
{
d: "m19.5 8.25-7.5 7.5-7.5-7.5",
},
],
class: "ms-2 size-4 flex-shrink-0",
viewBox: "0 0 24 24", viewBox: "0 0 24 24",
fill: "none", fill: "none",
strokeWidth: "1.5", strokeWidth: "1.5",

View file

@ -1,8 +1,8 @@
--- ---
// Importing necessary components // Importing necessary components
import Meta from "@components/Meta.astro"; import Meta from "@components/Meta.astro";
import Navbar from "@components/sections/Navbar.astro"; import Navbar from "@components/sections/navbar&footer/NavbarMegaMenu.astro";
import FooterSection from "@components/sections/FooterSection.astro"; import FooterSection from "@components/sections/navbar&footer/FooterSection.astro";
import { SITE } from "@data/constants"; import { SITE } from "@data/constants";
// Setting expected props // Setting expected props

View file

@ -1,7 +1,7 @@
--- ---
// Import the necessary components // Import the necessary components
import MainLayout from "@/layouts/MainLayout.astro"; import MainLayout from "@/layouts/MainLayout.astro";
import ContactSection from "@components/sections/ContactSection.astro"; import ContactSection from "@components/sections/misc/ContactSection.astro";
import { SITE } from "@data/constants"; import { SITE } from "@data/constants";
const pageTitle: string = `Contact | ${SITE.title}`; const pageTitle: string = `Contact | ${SITE.title}`;

View file

@ -8,7 +8,7 @@ import FeaturesGeneral from "@components/sections/features/FeaturesGeneral.astro
import FeaturesNavs from "@components/sections/features/FeaturesNavs.astro"; import FeaturesNavs from "@components/sections/features/FeaturesNavs.astro";
import TestimonialsSection from "@components/sections/testimonials/TestimonialsSection.astro"; import TestimonialsSection from "@components/sections/testimonials/TestimonialsSection.astro";
import PricingSection from "@components/sections/pricing/PricingSection.astro"; import PricingSection from "@components/sections/pricing/PricingSection.astro";
import FAQ from "@components/sections/FAQ.astro"; import FAQ from "@components/sections/misc/FAQ.astro";
import AnnouncementBanner from "@components/ui/banners/AnnouncementBanner.astro"; import AnnouncementBanner from "@components/ui/banners/AnnouncementBanner.astro";
import heroImage from "@images/hero-image.avif"; import heroImage from "@images/hero-image.avif";
import faqs from "@data/fr/faqs.json"; import faqs from "@data/fr/faqs.json";

View file

@ -8,7 +8,7 @@ import FeaturesGeneral from "@components/sections/features/FeaturesGeneral.astro
import FeaturesNavs from "@components/sections/features/FeaturesNavs.astro"; import FeaturesNavs from "@components/sections/features/FeaturesNavs.astro";
import TestimonialsSection from "@components/sections/testimonials/TestimonialsSection.astro"; import TestimonialsSection from "@components/sections/testimonials/TestimonialsSection.astro";
import PricingSection from "@components/sections/pricing/PricingSection.astro"; import PricingSection from "@components/sections/pricing/PricingSection.astro";
import FAQ from "@components/sections/FAQ.astro"; import FAQ from "@components/sections/misc/FAQ.astro";
import AnnouncementBanner from "@components/ui/banners/AnnouncementBanner.astro"; import AnnouncementBanner from "@components/ui/banners/AnnouncementBanner.astro";
import heroImage from "@images/hero-image.avif"; import heroImage from "@images/hero-image.avif";
import faqs from "@data/faqs.json"; import faqs from "@data/faqs.json";

View file

@ -27,9 +27,7 @@ const { product } = Astro.props;
const pageTitle: string = `${product.data.title} | ${SITE.title}`; const pageTitle: string = `${product.data.title} | ${SITE.title}`;
--- ---
<MainLayout <MainLayout title={pageTitle}>
title={pageTitle}
>
<div id="overlay" class="fixed inset-0 bg-neutral-200 dark:bg-neutral-800"> <div id="overlay" class="fixed inset-0 bg-neutral-200 dark:bg-neutral-800">
</div> </div>
@ -245,130 +243,143 @@ const pageTitle: string = `${product.data.title} | ${SITE.title}`;
</div> </div>
</div> </div>
</MainLayout> </MainLayout>
<script is:inline src="/scripts/vendor/gsap/gsap.min.js"></script>
<script>
window.addEventListener("load", () => {
if (window.gsap) {
const gsap = window.gsap;
gsap.set("#fadeText", {
autoAlpha: 0,
y: 50,
willChange: "transform, opacity",
});
gsap.set("#fadeInUp", {
autoAlpha: 0,
y: 50,
willChange: "transform, opacity",
});
gsap.set("#fadeInMoveRight", {
autoAlpha: 0,
x: 300,
willChange: "transform, opacity",
});
let timeline = gsap.timeline({ defaults: { overwrite: "auto" } }); <script>
import { gsap } from "gsap";
timeline.to("#fadeText", { type AnimationSettings = {
duration: 1.5, autoAlpha?: number;
autoAlpha: 1, y?: number;
y: 0, x?: number;
delay: 1, willChange?: string;
ease: "power2.out", };
});
timeline.to( function setElementAnimationDefaults(
"#fadeInUp", id: string,
{ duration: 1.5, autoAlpha: 1, y: 0, ease: "power2.out" }, settings: AnimationSettings
"-=1.2", ) {
); gsap.set(id, settings);
}
timeline.to( setElementAnimationDefaults("#fadeText", {
"#fadeInMoveRight", autoAlpha: 0,
{ duration: 1.5, autoAlpha: 1, x: 0, ease: "power2.inOut" }, y: 50,
"-=1.4", willChange: "transform, opacity",
); });
timeline.to("#overlay", { duration: 1, autoAlpha: 0, delay: 0.2 }); setElementAnimationDefaults("#fadeInUp", {
} autoAlpha: 0,
}); y: 50,
</script> willChange: "transform, opacity",
<script> });
document.addEventListener("DOMContentLoaded", function () {
function setButtonInactive(btn: any, activeButton: any) {
if (btn !== activeButton) {
btn.classList.remove(
"active",
"bg-neutral-100",
"hover:border-transparent",
"dark:bg-white/[.05]",
);
const tabId = btn.getAttribute("data-target"); setElementAnimationDefaults("#fadeInMoveRight", {
if (tabId) { autoAlpha: 0,
const contentElement = document.querySelector(tabId); x: 300,
if (contentElement) { willChange: "transform, opacity",
contentElement.classList.add("hidden"); });
}
}
changeHeadingStyle( let timeline = gsap.timeline({ defaults: { overwrite: "auto" } });
btn,
["text-neutral-800", "dark:text-neutral-200"],
["text-orange-400", "dark:text-orange-300"],
);
}
}
function activateButton(button: any) { timeline.to("#fadeText", {
button.classList.add( duration: 1.5,
autoAlpha: 1,
y: 0,
delay: 1,
ease: "power2.out",
});
timeline.to(
"#fadeInUp",
{ duration: 1.5, autoAlpha: 1, y: 0, ease: "power2.out" },
"-=1.2"
);
timeline.to(
"#fadeInMoveRight",
{ duration: 1.5, autoAlpha: 1, x: 0, ease: "power2.inOut" },
"-=1.4"
);
timeline.to("#overlay", { duration: 1, autoAlpha: 0, delay: 0.2 });
</script>
<script>
document.addEventListener("DOMContentLoaded", function () {
function setButtonInactive(btn: any, activeButton: any) {
if (btn !== activeButton) {
btn.classList.remove(
"active", "active",
"bg-neutral-100", "bg-neutral-100",
",hover:border-transparent", "hover:border-transparent",
"dark:bg-white/[.05]", "dark:bg-white/[.05]"
); );
const tabId = button.getAttribute("data-target"); const tabId = btn.getAttribute("data-target");
if (tabId) { if (tabId) {
const contentElementToShow = document.querySelector(tabId); const contentElement = document.querySelector(tabId);
if (contentElementToShow) { if (contentElement) {
contentElementToShow.classList.remove("hidden"); contentElement.classList.add("hidden");
} }
} }
changeHeadingStyle( changeHeadingStyle(
button, btn,
["text-orange-400", "dark:text-orange-300"],
["text-neutral-800", "dark:text-neutral-200"], ["text-neutral-800", "dark:text-neutral-200"],
["text-orange-400", "dark:text-orange-300"]
); );
} }
}
function changeHeadingStyle( function activateButton(button: any) {
button: any, button.classList.add(
addClasses: any, "active",
removeClasses: any, "bg-neutral-100",
) { ",hover:border-transparent",
let heading = button.querySelector("span"); "dark:bg-white/[.05]"
if (heading) { );
heading.classList.remove(...removeClasses);
heading.classList.add(...addClasses); const tabId = button.getAttribute("data-target");
if (tabId) {
const contentElementToShow = document.querySelector(tabId);
if (contentElementToShow) {
contentElementToShow.classList.remove("hidden");
} }
} }
const tabButtons = document.querySelectorAll("[data-target]"); changeHeadingStyle(
button,
["text-orange-400", "dark:text-orange-300"],
["text-neutral-800", "dark:text-neutral-200"]
);
}
if (tabButtons.length > 0) { function changeHeadingStyle(
changeHeadingStyle( button: any,
tabButtons[0], addClasses: any,
["text-orange-400", "dark:text-orange-300"], removeClasses: any
[], ) {
); let heading = button.querySelector("span");
if (heading) {
heading.classList.remove(...removeClasses);
heading.classList.add(...addClasses);
} }
}
tabButtons.forEach((button) => { const tabButtons = document.querySelectorAll("[data-target]");
button.addEventListener("click", () => {
tabButtons.forEach((btn) => setButtonInactive(btn, button)); if (tabButtons.length > 0) {
activateButton(button); changeHeadingStyle(
}); tabButtons[0],
["text-orange-400", "dark:text-orange-300"],
[]
);
}
tabButtons.forEach((button) => {
button.addEventListener("click", () => {
tabButtons.forEach((btn) => setButtonInactive(btn, button));
activateButton(button);
}); });
}); });
</script> });
</script>