Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Technologies section #85

Merged
merged 13 commits into from
Sep 4, 2023
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions src/locales/en/home.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,21 @@
"after-link": "!"
}
},
"technologies": {
"title": "The [technologies] we use",
"framework": {
"title": "Framework: [SvelteKit]",
"desc": "Svelte, with its SvelteKit metaframework, gives us unrivalled performance and a simple, pleasant development experience."
},
"database": {
"title": "Database: [PostgreSQL]",
"desc": "PostgreSQL is a powerful, reliable and proven database, perfect for storing your data securely."
},
"infrastructure": {
"title": "Infrastructure: [Vercel]",
"desc": "Vercel is a high-performance cloud platform designed for SvelteKit, enabling us to host your project in the best possible conditions."
}
},
"cta-bottom": {
"subtitle": "Ready to get started?",
"title": "Let's build your [dream] project"
Expand Down
15 changes: 15 additions & 0 deletions src/locales/fr/home.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,21 @@
"after-link": " !"
}
},
"technologies": {
"title": "Les [technologies] que nous utilisons",
"framework": {
"title": "Framework : [SvelteKit]",
"desc": "Svelte, avec son metaframework SvelteKit, nous permet de bénéficier de performances inégalées et d'une expérience de développement simple et agréable."
},
"database": {
"title": "Base de données : [PostgreSQL]",
"desc": "PostgreSQL est une base de données puissante, fiable et éprouvée, parfaite pour stocker vos données en toute sécurité."
},
"infrastructure": {
"title": "Infrastructure : [Vercel]",
"desc": "Vercel est une plateforme cloud qui nous permet d'héberger votre projet de façon très performante, car conçue pour SvelteKit."
}
},
"cta-bottom": {
"subtitle": "Prêt à vous lancer ?",
"title": "Construisons votre projet [de rêve]"
Expand Down
186 changes: 185 additions & 1 deletion src/routes/+page.svelte
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<script lang="ts">
import { ROOT_URL } from "$config";
import type { SvelteComponent } from "svelte";
import { onMount, type SvelteComponent } from "svelte";
import type { SvelteHTMLElements } from "svelte/elements";
import MagneticElement from "$shells/MagneticElement.svelte";
import Mouse3DTilting from "$shells/Mouse3DTilting.svelte";
Expand All @@ -15,6 +15,7 @@
CodeBracket,
DevicePhoneMobile
} from "@inqling/svelte-icons/heroicon-24-solid";
import { Postgresql, Svelte, Vercel } from "@inqling/svelte-icons/simple-icons";
import { i, language } from "@inlang/sdk-js";
import { c } from "$utils/inlang-color";
import resolveConfig from "tailwindcss/resolveConfig";
Expand All @@ -32,6 +33,7 @@
}[] = [];
let solutionsSections: { title: string; description: string }[] = [];
let solutions: typeof solutionsSections = [];
let technologiesSections: typeof processSections & { brandColor: string }[] = [];
$: if (language) {
processSections = [
{
Expand Down Expand Up @@ -77,6 +79,50 @@
}
];
solutions = solutionsSections;
technologiesSections = [
{
title: c(i("home.technologies.framework.title")),
icon: Svelte,
brandColor: "#FF3E00",
description: i("home.technologies.framework.desc")
},
{
title: c(i("home.technologies.database.title")),
icon: Postgresql,
brandColor: "#4169E1",
description: i("home.technologies.database.desc")
},
{
title: c(i("home.technologies.infrastructure.title")),
icon: Vercel,
brandColor: "#FFFFFF",
description: i("home.technologies.infrastructure.desc")
}
];
}

// Technologies cards
let technoCards: HTMLElement;
let technoIcons: HTMLElement;

function getOffset(
totalPoints: number,
pointNumber: number,
radius: number = 60,
clockwise: boolean = true
) {
if (pointNumber > totalPoints) {
throw new Error("Point number cannot exceed total number of points.");
}

const baseAngle = ((2 * Math.PI) / totalPoints) * pointNumber;

const angle = clockwise ? baseAngle : 2 * Math.PI - baseAngle;

return {
x: radius * Math.cos(angle),
y: radius * Math.sin(angle)
};
}

// Keep only 3 solutions sections if screen is too small
Expand All @@ -88,6 +134,92 @@
solutions = solutionsSections;
}
}

// Auto-scroll technologies cards
const DELAY = 5000;
let currentCard = 0;

function hoverIcon(index: number) {
const icons = technoIcons.children;
if (!icons || icons.length < index) return;
const icon = icons[index];
if (!icon) return;

icon.classList.add("is-selected");
for (let i = 0; i < icons.length; i++) {
if (i === index) continue;
icons[i]?.classList.remove("is-selected");
}
}

function scrollToCard(index: number) {
const cards = technoCards?.children;
if (!cards || cards.length < index) return;
const card = cards[index];
if (!card) return;

technoCards.scrollTo({
left: card.clientWidth * index,
behavior: "smooth"
});
}

onMount(() => {
// === Auto-scroll technologies cards ===
// Initial checks
const cards = technoCards?.children;
if (!cards || cards.length < 2) return;

// Hover the first icon on load, otherwise
// no icon is hovered until the first scroll
hoverIcon(currentCard);

// Auto-scroll function
const autoScroll = () => {
if (currentCard === cards.length - 1) {
currentCard = 0;
} else {
currentCard++;
}
scrollToCard(currentCard);
};

// Initial interval definition, start auto-scrolling
let interval = setInterval(autoScroll, DELAY);

// Stop the interval on hover of the cards
technoCards.addEventListener("mouseenter", () => {
clearInterval(interval);
});

// Restart the interval on mouse leave
technoCards.addEventListener("mouseleave", () => {
interval = setInterval(autoScroll, DELAY);
});

// Add listeners to the icons to start/stop the interval on hover
[...technoIcons.children].forEach(icon => {
icon.addEventListener("mouseenter", () => {
clearInterval(interval);
});

icon.addEventListener("mouseleave", () => {
interval = setInterval(autoScroll, DELAY);
});
});

// Scroll handler to update the hovered icon depending on
// the card we scrolled to
technoCards.addEventListener("scrollend", () => {
const scrollDistance = technoCards.scrollLeft;
const containerWidth = technoCards.clientWidth;
currentCard = Math.round(scrollDistance / containerWidth);
hoverIcon(currentCard);
});

// On destroy, clear the interval
return () => clearInterval(interval);
});
</script>

<!-- Window bindings -->
Expand Down Expand Up @@ -307,6 +439,58 @@
</div>
</Section>

<!-- Technologies -->
<Section id="technologies">
<svelte:fragment slot="title">
<!-- eslint-disable-next-line svelte/no-at-html-tags -->
{@html c(i("home.technologies.title"))}
</svelte:fragment>
<div class="flex flex-col items-center gap-8 sm:flex-row">
<!-- Left part -->
<div
bind:this={technoCards}
class="flex max-w-full snap-x snap-mandatory gap-8 overflow-x-auto py-4 child:snap-start sm:max-w-none"
>
{#each technologiesSections as techno}
<div class="flex min-w-full flex-col gap-4 rounded-3xl bg-gray-700 p-8">
<!-- eslint-disable-next-line svelte/no-at-html-tags -->
<h3 class="text-xl font-medium">{@html techno.title}</h3>
<p class="text-lg text-gray-200">
{techno.description}
</p>
</div>
{/each}
</div>

<!-- Right part -->
<div class="aspect-square h-56 lg:h-48">
<div
bind:this={technoIcons}
class="relative flex h-full w-full items-center justify-center -rotate-45"
>
{#each technologiesSections as techno, index}
{@const { x, y } = getOffset(technologiesSections.length, index, 50, false)}
<button
style="transform: translate({x}%, {y}%);"
class="group absolute flex aspect-square h-1/2 items-center justify-center rounded-full bg-gray-400/75 transition-all duration-700
hover:bg-gray-500 hover:scale-110
[&.is-selected]:z-10 [&.is-selected]:bg-gray-600 [&.is-selected]:scale-110"
on:click={() => scrollToCard(index)}
>
<svelte:component
this={techno.icon}
style="--brand-color: {techno.brandColor}"
class="w-1/2 drop-shadow-md transition-all duration-700 rotate-45
group-hover:fill-[var(--brand-color)] group-hover:scale-110
group-[.is-selected]:fill-[var(--brand-color)] group-[.is-selected]:scale-110"
/>
</button>
{/each}
</div>
</div>
</div>
</Section>

<!-- Bottom CTA -->
<Section>
<div class="my-32 flex flex-col items-center justify-center">
Expand Down