mirror of
https://github.com/Lapikud/lapikud.github.io.git
synced 2026-08-08 19:49:13 +00:00
updated and fixed most pages + navbar update on home page
This commit is contained in:
@@ -1,52 +1,41 @@
|
|||||||
# Workshops & courses conducted by Lapikud members
|
# Workshops & courses conducted by Lapikud members
|
||||||
#
|
#
|
||||||
- title:
|
- title:
|
||||||
en: Go Programming Language Workshop
|
en: C workshop
|
||||||
est: Go programmeerimiskeele töötuba
|
est: C koolitus
|
||||||
description:
|
description:
|
||||||
en: Introduction to Go through practical exercises. Covered basic syntax, goroutines, channels and concurrency with real-world examples.
|
en: Three-part introductory C workshop series for beginners and early-stage programmers. Covered core programming foundations and practical C basics in Estonian at TalTech IT College.
|
||||||
est: Sissejuhatus Go keelde läbi praktiliste ülesannete. Kaetud on põhisüntaks, goroutiinid, kanalid ja konkurentsus reaaleluliste näidete kaudu.
|
est: Kolmeosaline C programmeerimiskeele koolitus algajatele ja varasema kogemusega huvilistele. Käsitleti programmeerimise alusteadmisi ja C keele põhiteemasid praktiliste näidete kaudu TalTech IT Kolledžis.
|
||||||
mentor:
|
mentor:
|
||||||
en: Henri Karba
|
en: Lapikud mentors
|
||||||
est: Henri Karba
|
est: MTÜ Lapikud mentorid
|
||||||
duration:
|
duration:
|
||||||
en: 3 × 2h
|
en: 3 × 1h 30min
|
||||||
est: 3 × 2h
|
est: 3 × 1h 30min
|
||||||
date:
|
date:
|
||||||
en: March 2025
|
en: March 2026
|
||||||
est: Märts 2025
|
est: Märts 2026
|
||||||
gallery: 3
|
gallery:
|
||||||
|
- temp1.jpg
|
||||||
|
- temp2.jpg
|
||||||
|
|
||||||
- title:
|
- title:
|
||||||
en: Figma Design Workshop
|
en: C workshop
|
||||||
est: Figma disainitöötuba
|
est: C koolitus
|
||||||
description:
|
description:
|
||||||
en: Over two days we learned the Figma workflow, covering components, auto-layout, prototyping and presenting to clients.
|
en: Three-part introductory C workshop series for beginners and early-stage programmers. Covered core programming foundations and practical C basics in Estonian at TalTech IT College.
|
||||||
est: Kahe päeva jooksul õppisime Figma töövoogu, komponendid, auto-layout, prototüüpimine ja klientidele esitamine.
|
est: Kolmeosaline C programmeerimiskeele koolitus algajatele ja varasema kogemusega huvilistele. Käsitleti programmeerimise alusteadmisi ja C keele põhiteemasid praktiliste näidete kaudu TalTech IT Kolledžis.
|
||||||
mentor:
|
mentor:
|
||||||
en: Maris Tamm
|
en: Lapikud mentors
|
||||||
est: Maris Tamm
|
est: MTÜ Lapikud mentorid
|
||||||
duration:
|
duration:
|
||||||
en: 2 days
|
en: 3 × 1h 30min
|
||||||
est: 2 päeva
|
est: 3 × 1h 30min
|
||||||
date:
|
date:
|
||||||
en: November 2024
|
en: March 2026
|
||||||
est: Nov 2024
|
est: Märts 2026
|
||||||
gallery: 2
|
gallery:
|
||||||
|
- temp1.jpg
|
||||||
|
- temp2.jpg
|
||||||
|
- temp3.jpg
|
||||||
|
|
||||||
- title:
|
|
||||||
en: Web Security Basics
|
|
||||||
est: Veebiturvalisuse alused
|
|
||||||
description:
|
|
||||||
en: Practical seminar on OWASP Top 10 attacks and defense methods. Lab work involved fixing real vulnerabilities in a controlled environment.
|
|
||||||
est: Praktiline seminar OWASP Top 10 rünnakute ja kaitsmismeetodite kohta. Laboris lahendati päris haavatavusi kontrollitud keskkonnas.
|
|
||||||
mentor:
|
|
||||||
en: Juku Rebane
|
|
||||||
est: Juku Rebane
|
|
||||||
duration:
|
|
||||||
en: 4 hours
|
|
||||||
est: 4 tundi
|
|
||||||
date:
|
|
||||||
en: September 2024
|
|
||||||
est: Sept 2024
|
|
||||||
gallery: 4
|
|
||||||
|
|||||||
BIN
public/assets/workshops/temp1.jpg
Normal file
BIN
public/assets/workshops/temp1.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 96 KiB |
BIN
public/assets/workshops/temp2.jpg
Normal file
BIN
public/assets/workshops/temp2.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 241 KiB |
11
src/app.css
11
src/app.css
@@ -1,6 +1,6 @@
|
|||||||
/* Global stylesheet */
|
/* Global stylesheet */
|
||||||
|
|
||||||
@import url("https://fonts.googleapis.com/css2?family=Unbounded:wght@300;400;500;600;700&display=swap");
|
@import url("https://fonts.googleapis.com/css2?family=Lato:wght@300;400;700;900&display=swap");
|
||||||
@import "tailwindcss";
|
@import "tailwindcss";
|
||||||
|
|
||||||
/* Overwriting tailwindcss specific variables */
|
/* Overwriting tailwindcss specific variables */
|
||||||
@@ -46,12 +46,7 @@
|
|||||||
--spacing-6: 48px;
|
--spacing-6: 48px;
|
||||||
|
|
||||||
/* type scale (fluid base + modular sizes) */
|
/* type scale (fluid base + modular sizes) */
|
||||||
--fs-base: clamp(0.95rem, 0.9vw + 0.9rem, 1rem);
|
--fs-base: clamp(1.1rem, 1vw + 1rem, 1.2rem);
|
||||||
--fs-xs: 0.85rem;
|
|
||||||
--fs-sm: 0.92rem;
|
|
||||||
--fs-md: 1rem;
|
|
||||||
--fs-lg: 1.125rem;
|
|
||||||
--fs-xl: 1.5rem;
|
|
||||||
--line-height: 1.6;
|
--line-height: 1.6;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -62,7 +57,7 @@
|
|||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
html {
|
html {
|
||||||
font-family: "Unbounded", ui-sans-serif, system-ui, -apple-system, "Segoe UI",
|
font-family: "Lato", ui-sans-serif, system-ui, -apple-system, "Segoe UI",
|
||||||
Roboto, "Helvetica Neue", Arial;
|
Roboto, "Helvetica Neue", Arial;
|
||||||
-webkit-font-smoothing: antialiased;
|
-webkit-font-smoothing: antialiased;
|
||||||
-moz-osx-font-smoothing: grayscale;
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
} = $props();
|
} = $props();
|
||||||
|
|
||||||
let isOpen = $state(false);
|
let isOpen = $state(false);
|
||||||
|
let panelElement = $state(null);
|
||||||
|
|
||||||
const toggle = () => (isOpen = !isOpen);
|
const toggle = () => (isOpen = !isOpen);
|
||||||
const close = () => (isOpen = false);
|
const close = () => (isOpen = false);
|
||||||
@@ -21,6 +22,26 @@
|
|||||||
return;
|
return;
|
||||||
close();
|
close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
$effect(() => {
|
||||||
|
if (!isOpen) return;
|
||||||
|
|
||||||
|
const handleDocumentClick = (event) => {
|
||||||
|
const target = event.target;
|
||||||
|
if (!(target instanceof HTMLElement) || !panelElement) return;
|
||||||
|
|
||||||
|
if (
|
||||||
|
panelElement.contains(target) &&
|
||||||
|
target.closest("button, a, [role='menuitem']")
|
||||||
|
) {
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
document.addEventListener("click", handleDocumentClick);
|
||||||
|
return () => document.removeEventListener("click", handleDocumentClick);
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="relative inline-block" onfocusout={handleFocusOut}>
|
<div class="relative inline-block" onfocusout={handleFocusOut}>
|
||||||
@@ -38,6 +59,7 @@
|
|||||||
<div
|
<div
|
||||||
class="{panelClass} absolute mt-2 rounded-md shadow-lg overflow-visible flex flex-col"
|
class="{panelClass} absolute mt-2 rounded-md shadow-lg overflow-visible flex flex-col"
|
||||||
style:visibility={isOpen ? "visible" : "hidden"}
|
style:visibility={isOpen ? "visible" : "hidden"}
|
||||||
|
bind:this={panelElement}
|
||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
{@render children?.()}
|
{@render children?.()}
|
||||||
|
|||||||
@@ -44,63 +44,19 @@
|
|||||||
return paths.includes(currentPath);
|
return paths.includes(currentPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
function isHomePage() {
|
|
||||||
return currentPath === "/";
|
|
||||||
}
|
|
||||||
|
|
||||||
function headerShellClass() {
|
|
||||||
return isHomePage()
|
|
||||||
? "bg-transparent"
|
|
||||||
: "border-b-2 border-orange-500 bg-gray-900 backdrop-blur-md";
|
|
||||||
}
|
|
||||||
|
|
||||||
function headerRowClass() {
|
|
||||||
return isHomePage()
|
|
||||||
? "flex min-h-16 items-center w-full relative gap-4"
|
|
||||||
: "flex min-h-16 items-center gap-4";
|
|
||||||
}
|
|
||||||
|
|
||||||
function headerWrapClass() {
|
|
||||||
return isHomePage()
|
|
||||||
? "ml-auto flex w-full self-stretch items-center justify-end md:w-auto md:border-b-3 md:border-white backdrop-blur-lg bg-black/35 pr-(--page-padding-inline)"
|
|
||||||
: "flex items-center justify-end ml-auto w-full md:w-auto";
|
|
||||||
}
|
|
||||||
|
|
||||||
function desktopNavClass() {
|
|
||||||
return isHomePage()
|
|
||||||
? "z-10 hidden md:flex gap-1 justify-end text-lg"
|
|
||||||
: "z-10 hidden md:flex items-center gap-1 justify-end text-lg";
|
|
||||||
}
|
|
||||||
|
|
||||||
function navButtonClass(active = false) {
|
function navButtonClass(active = false) {
|
||||||
if (isHomePage()) {
|
|
||||||
return active
|
|
||||||
? "border-transparent text-orange-500 bg-transparent"
|
|
||||||
: "border-transparent bg-transparent text-white/75 hover:text-orange-500";
|
|
||||||
}
|
|
||||||
|
|
||||||
return active
|
return active
|
||||||
? "border-transparent text-orange-500 hover:bg-white/5"
|
? "border-transparent text-orange-500 hover:bg-white/5"
|
||||||
: "border-transparent text-white/75 hover:bg-white/5";
|
: "border-transparent text-white/75 hover:bg-white/5";
|
||||||
}
|
}
|
||||||
|
|
||||||
function dropdownButtonClass(active = false) {
|
function dropdownButtonClass(active = false) {
|
||||||
if (isHomePage()) {
|
|
||||||
return active
|
|
||||||
? "border-transparent text-orange-500 bg-transparent"
|
|
||||||
: "border-transparent bg-transparent text-white/75 hover:text-orange-500";
|
|
||||||
}
|
|
||||||
|
|
||||||
return active
|
return active
|
||||||
? "border-transparent text-orange-500 hover:bg-white/5"
|
? "border-transparent text-orange-500 hover:bg-white/5"
|
||||||
: "border-transparent text-white/75 hover:bg-white/5";
|
: "border-transparent text-white/75 hover:bg-white/5";
|
||||||
}
|
}
|
||||||
|
|
||||||
function languageButtonClass() {
|
function languageButtonClass() {
|
||||||
if (isHomePage()) {
|
|
||||||
return "ml-3 border-transparent bg-transparent px-3 py-1 text-sm text-white/75 hover:text-orange-500";
|
|
||||||
}
|
|
||||||
|
|
||||||
return "ml-3 border-transparent bg-transparent px-3 py-1 text-sm text-white/75 hover:bg-white/5";
|
return "ml-3 border-transparent bg-transparent px-3 py-1 text-sm text-white/75 hover:bg-white/5";
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -217,21 +173,19 @@
|
|||||||
</style>
|
</style>
|
||||||
|
|
||||||
<header class="fixed inset-x-0 top-0 z-50 text-white">
|
<header class="fixed inset-x-0 top-0 z-50 text-white">
|
||||||
<div class={headerShellClass()}>
|
<div class="border-b-2 border-orange-500 bg-gray-900 backdrop-blur-md">
|
||||||
<Container maxWidth={true} center={!isHomePage()} class="py-0">
|
<Container class="py-0">
|
||||||
{#if $text && Object.keys($text).length > 0}
|
{#if $text && Object.keys($text).length > 0}
|
||||||
<div class={headerRowClass()}>
|
<div class="flex min-h-16 items-center gap-4">
|
||||||
{#if !isHomePage()}
|
<Button
|
||||||
<Button
|
onClick={() => handleNavigation("/")}
|
||||||
onClick={() => handleNavigation("/")}
|
class="nav-logo-button border-transparent bg-transparent px-0 py-3"
|
||||||
class="nav-logo-button border-transparent bg-transparent px-0 py-3"
|
>
|
||||||
>
|
<img src="/assets/logo.svg" alt="Lapikud logo" class="h-12 md:h-14 w-auto" />
|
||||||
<img src="/assets/logo.svg" alt="Lapikud logo" class="h-12 md:h-14 w-auto" />
|
</Button>
|
||||||
</Button>
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
<div class={headerWrapClass()}>
|
<div class="ml-auto flex w-full items-center justify-end md:w-auto">
|
||||||
<nav class={desktopNavClass()}>
|
<nav class="z-10 hidden items-center gap-1 justify-end text-lg md:flex">
|
||||||
<Dropdown
|
<Dropdown
|
||||||
name={$text.about}
|
name={$text.about}
|
||||||
buttonClass={`${dropdownButtonClass(isInGroup(aboutRoutes))} ${isInGroup(aboutRoutes) ? "nav-active-parent" : ""}`}
|
buttonClass={`${dropdownButtonClass(isInGroup(aboutRoutes))} ${isInGroup(aboutRoutes) ? "nav-active-parent" : ""}`}
|
||||||
@@ -309,7 +263,7 @@
|
|||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
class={languageButtonClass()}
|
class="ml-3 border-transparent bg-transparent px-3 py-1 text-sm text-white/75 hover:bg-white/5"
|
||||||
onClick={() => switchLanguageRoute($currentLang === "est" ? "en" : "est")}
|
onClick={() => switchLanguageRoute($currentLang === "est" ? "en" : "est")}
|
||||||
>
|
>
|
||||||
{$currentLang === "est" ? "EN" : "EST"}
|
{$currentLang === "est" ? "EN" : "EST"}
|
||||||
|
|||||||
@@ -11,7 +11,23 @@
|
|||||||
let { routes } = $props();
|
let { routes } = $props();
|
||||||
|
|
||||||
let currentPath = $state(window.location.pathname);
|
let currentPath = $state(window.location.pathname);
|
||||||
let CurrentComponent = $derived(routes[currentPath] || routes["/"]);
|
let CurrentComponent = $state(null);
|
||||||
|
|
||||||
|
async function resolveCurrentComponent(path) {
|
||||||
|
const routeEntry = routes[path] || routes["/"];
|
||||||
|
|
||||||
|
if (typeof routeEntry === "function") {
|
||||||
|
try {
|
||||||
|
const module = await routeEntry();
|
||||||
|
CurrentComponent = module?.default || null;
|
||||||
|
} catch {
|
||||||
|
CurrentComponent = null;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
CurrentComponent = routeEntry || null;
|
||||||
|
}
|
||||||
|
|
||||||
function navigate(path) {
|
function navigate(path) {
|
||||||
window.history.pushState({}, "", path);
|
window.history.pushState({}, "", path);
|
||||||
@@ -46,6 +62,10 @@
|
|||||||
|
|
||||||
return () => window.removeEventListener("click", handleClick);
|
return () => window.removeEventListener("click", handleClick);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$effect(() => {
|
||||||
|
resolveCurrentComponent(currentPath);
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if CurrentComponent}
|
{#if CurrentComponent}
|
||||||
|
|||||||
@@ -54,12 +54,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="pointer-events-none absolute inset-y-0 left-1/2 w-screen -translate-x-1/2 bg-linear-to-r from-black/65 via-black/35 to-transparent" aria-hidden="true"></div>
|
<div class="pointer-events-none absolute inset-y-0 left-1/2 w-screen -translate-x-1/2 bg-linear-to-r from-black/65 via-black/35 to-transparent" aria-hidden="true"></div>
|
||||||
<div class="relative z-20 flex min-h-screen w-full items-stretch py-8 md:py-12">
|
<div class="relative z-20 flex min-h-screen w-full items-stretch py-8 md:py-12">
|
||||||
<div class="flex w-full max-w-115 flex-col justify-between">
|
<div class="flex w-full max-w-115 flex-col justify-end">
|
||||||
<Image
|
|
||||||
src="/assets/logo.svg"
|
|
||||||
alt={$text["hero"]?.logoAlt || "MTÜ Lapikud"}
|
|
||||||
class="w-full max-w-65 md:max-w-80 h-auto pt-5 md:pt-10"
|
|
||||||
/>
|
|
||||||
<div class="flex flex-col items-start gap-5">
|
<div class="flex flex-col items-start gap-5">
|
||||||
<p class="w-full max-w-110 text-left text-lg leading-[165%] text-white drop-shadow-[0_2px_8px_rgba(0,0,0,0.6)] md:max-w-125 md:text-xl">
|
<p class="w-full max-w-110 text-left text-lg leading-[165%] text-white drop-shadow-[0_2px_8px_rgba(0,0,0,0.6)] md:max-w-125 md:text-xl">
|
||||||
{$text["hero"]?.description || "MTÜ Lapikud on Tallinna Tehnikaülikooli Tarkvaraarendusklubi, mis ühendab endisi ja praegusi IT huvilisi tudengeid."}
|
{$text["hero"]?.description || "MTÜ Lapikud on Tallinna Tehnikaülikooli Tarkvaraarendusklubi, mis ühendab endisi ja praegusi IT huvilisi tudengeid."}
|
||||||
|
|||||||
@@ -105,7 +105,7 @@
|
|||||||
</Section>
|
</Section>
|
||||||
|
|
||||||
<Section padding="large">
|
<Section padding="large">
|
||||||
<div class="mb-12">
|
<div class="mb-22">
|
||||||
<p class="mb-3 text-[0.72rem] font-bold uppercase tracking-[0.12em] text-orange-500">{$text["portfolio"]?.label || "Tehtud tööd"}</p>
|
<p class="mb-3 text-[0.72rem] font-bold uppercase tracking-[0.12em] text-orange-500">{$text["portfolio"]?.label || "Tehtud tööd"}</p>
|
||||||
<h1 class="m-0 max-w-[20ch] text-[clamp(1.9rem,4vw,3.15rem)] leading-[1.08] tracking-[-0.03em]">{$text["portfolio"]?.title || "Projektid, mis räägivad enda eest."}</h1>
|
<h1 class="m-0 max-w-[20ch] text-[clamp(1.9rem,4vw,3.15rem)] leading-[1.08] tracking-[-0.03em]">{$text["portfolio"]?.title || "Projektid, mis räägivad enda eest."}</h1>
|
||||||
<p class="mt-4 max-w-[62ch] leading-[1.65] text-gray-900">
|
<p class="mt-4 max-w-[62ch] leading-[1.65] text-gray-900">
|
||||||
@@ -114,13 +114,13 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{#if featuredProject}
|
{#if featuredProject}
|
||||||
<Card class="mb-5! grid! overflow-hidden! rounded-none! border-0! bg-white! p-0! ring-1 ring-gray-200 md:grid-cols-[1.25fr_1fr]!" variant="default">
|
<Card class="mb-5! grid! gap-4 rounded-none! border-0! bg-white! p-0! md:grid-cols-[1.25fr_1fr]! md:gap-6" variant="default">
|
||||||
<div class="min-h-[220px] overflow-hidden bg-gray-100/70 p-2 md:min-h-[280px]">
|
<div class="flex min-h-[220px] items-center justify-center md:min-h-[280px]">
|
||||||
{#if featuredProject.photo}
|
{#if featuredProject.photo}
|
||||||
<img
|
<img
|
||||||
src={projectImage(featuredProject)}
|
src={projectImage(featuredProject)}
|
||||||
alt={projectTitle(featuredProject)}
|
alt={projectTitle(featuredProject)}
|
||||||
class="block h-full w-full border border-gray-200 bg-white object-contain object-center shadow-[0_1px_2px_rgba(13,13,13,0.05)]"
|
class="block h-auto max-h-full w-auto max-w-full object-contain object-center shadow-[0_1px_2px_rgba(13,13,13,0.05)]"
|
||||||
on:error={(event) => handleImageError(event, featuredProject.photo)}
|
on:error={(event) => handleImageError(event, featuredProject.photo)}
|
||||||
/>
|
/>
|
||||||
{:else}
|
{:else}
|
||||||
@@ -128,17 +128,19 @@
|
|||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex flex-col justify-center gap-2 p-[clamp(1.5rem,3vw,2.5rem)]">
|
<div class="flex h-full flex-1 flex-col justify-between gap-[clamp(0.75rem,2vw,1.5rem)] p-[clamp(1.5rem,3vw,2.5rem)]">
|
||||||
<h2 class="m-0 text-[clamp(1.45rem,2.7vw,2rem)] leading-[1.2] tracking-[-0.02em]">{projectTitle(featuredProject)}</h2>
|
<div class="flex flex-col gap-[clamp(0.45rem,1vw,0.8rem)]">
|
||||||
{#if projectDescription(featuredProject)}
|
<h2 class="m-0 text-[clamp(1.45rem,2.7vw,2rem)] leading-[1.2] tracking-[-0.02em]">{projectTitle(featuredProject)}</h2>
|
||||||
<p class="m-0 text-[0.9rem] leading-[1.6] text-gray-900">{projectDescription(featuredProject)}</p>
|
{#if projectDescription(featuredProject)}
|
||||||
{/if}
|
<p class="m-0 text-[0.9rem] leading-[1.6] text-gray-900">{projectDescription(featuredProject)}</p>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
{#if featuredProject.url}
|
{#if featuredProject.url}
|
||||||
<a
|
<a
|
||||||
href={featuredProject.url}
|
href={featuredProject.url}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
class="mt-0.5 inline-flex w-fit items-center gap-1.5 font-semibold text-orange-500 hover:underline"
|
class="mt-auto inline-flex w-fit items-center gap-1.5 pt-1 font-semibold text-orange-500 hover:underline"
|
||||||
>
|
>
|
||||||
{$text["portfolio"]?.projectLink || "Vaata projekti"}
|
{$text["portfolio"]?.projectLink || "Vaata projekti"}
|
||||||
<ExternalLink size={16} />
|
<ExternalLink size={16} />
|
||||||
@@ -149,13 +151,13 @@
|
|||||||
{/if}
|
{/if}
|
||||||
<div class="ourwork-grid">
|
<div class="ourwork-grid">
|
||||||
{#each regularProjects as project}
|
{#each regularProjects as project}
|
||||||
<Card variant="default" class="ourwork-grid-card rounded-none! border-0! bg-white! p-0!">
|
<Card variant="default" class="ourwork-grid-card gap-4 rounded-none! border-0! bg-white! p-0!">
|
||||||
<div class="aspect-16/10 overflow-hidden bg-gray-100/70 p-2">
|
<div class="flex aspect-16/10 items-center justify-center">
|
||||||
{#if project.photo}
|
{#if project.photo}
|
||||||
<img
|
<img
|
||||||
src={projectImage(project)}
|
src={projectImage(project)}
|
||||||
alt={projectTitle(project)}
|
alt={projectTitle(project)}
|
||||||
class="block h-full w-full border border-gray-200 bg-white object-contain object-center shadow-[0_1px_2px_rgba(13,13,13,0.05)]"
|
class="block h-auto max-h-full w-auto max-w-full object-contain object-center shadow-[0_1px_2px_rgba(13,13,13,0.05)]"
|
||||||
on:error={(event) => handleImageError(event, project.photo)}
|
on:error={(event) => handleImageError(event, project.photo)}
|
||||||
/>
|
/>
|
||||||
{:else}
|
{:else}
|
||||||
@@ -163,13 +165,15 @@
|
|||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="ourwork-card-content flex h-full flex-1 flex-col gap-2 p-5">
|
<div class="ourwork-card-content flex h-full flex-1 flex-col justify-between gap-[clamp(0.55rem,1vw,0.95rem)] px-5 pb-5 pt-1.5">
|
||||||
<h3 class="ourwork-card-title m-0 text-base leading-[1.2] tracking-[-0.02em]">{projectTitle(project)}</h3>
|
<div class="flex flex-col gap-[clamp(0.4rem,0.9vw,0.75rem)]">
|
||||||
{#if projectDescription(project)}
|
<h3 class="ourwork-card-title m-0 text-base leading-[1.2] tracking-[-0.02em]">{projectTitle(project)}</h3>
|
||||||
<p class="ourwork-card-description m-0 text-[0.9rem] leading-[1.6] text-gray-900">{projectDescription(project)}</p>
|
{#if projectDescription(project)}
|
||||||
{:else}
|
<p class="ourwork-card-description m-0 text-[0.9rem] leading-[1.6] text-gray-900">{projectDescription(project)}</p>
|
||||||
<p class="ourwork-card-description m-0 text-[0.9rem] leading-[1.6] text-gray-900 italic">{$text["portfolio"]?.noDescription || "Kirjeldus lisamisel."}</p>
|
{:else}
|
||||||
{/if}
|
<p class="ourwork-card-description m-0 text-[0.9rem] leading-[1.6] text-gray-900 italic">{$text["portfolio"]?.noDescription || "Kirjeldus lisamisel."}</p>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
|
||||||
{#if project.url}
|
{#if project.url}
|
||||||
<a
|
<a
|
||||||
@@ -191,10 +195,10 @@
|
|||||||
|
|
||||||
<style>
|
<style>
|
||||||
.ourwork-grid {
|
.ourwork-grid {
|
||||||
display: flex;
|
display: grid;
|
||||||
flex-wrap: wrap;
|
gap: clamp(0.75rem, 1.5vw, 1.1rem);
|
||||||
gap: 1px;
|
align-items: stretch;
|
||||||
justify-content: center;
|
grid-template-columns: 1fr;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ourwork-grid :global(.ourwork-grid-card) {
|
.ourwork-grid :global(.ourwork-grid-card) {
|
||||||
@@ -205,37 +209,15 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ourwork-card-content {
|
|
||||||
min-height: 10.25rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ourwork-card-title {
|
|
||||||
min-height: calc(1.2em * 2);
|
|
||||||
display: -webkit-box;
|
|
||||||
line-clamp: 2;
|
|
||||||
-webkit-box-orient: vertical;
|
|
||||||
-webkit-line-clamp: 2;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ourwork-card-description {
|
|
||||||
min-height: calc(1.6em * 3);
|
|
||||||
display: -webkit-box;
|
|
||||||
line-clamp: 3;
|
|
||||||
-webkit-box-orient: vertical;
|
|
||||||
-webkit-line-clamp: 3;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (min-width: 768px) {
|
@media (min-width: 768px) {
|
||||||
.ourwork-grid :global(.ourwork-grid-card) {
|
.ourwork-grid {
|
||||||
width: calc((100% - 1px) / 2);
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 1200px) {
|
@media (min-width: 1200px) {
|
||||||
.ourwork-grid :global(.ourwork-grid-card) {
|
.ourwork-grid {
|
||||||
width: calc((100% - 2px) / 3);
|
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -51,7 +51,7 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="safe-area-navbar">
|
<div class="safe-area-navbar">
|
||||||
<Section class="overflow-hidden bg-[#0f0f0f]" padding="none" fullWidth={true} contentClass="relative !px-0">
|
<Section class="overflow-hidden bg-gray-900" padding="none" fullWidth={true} contentClass="relative !px-0">
|
||||||
<div class="relative overflow-hidden py-[clamp(3rem,8vw,6rem)]">
|
<div class="relative overflow-hidden py-[clamp(3rem,8vw,6rem)]">
|
||||||
<div class="relative z-10 mx-auto w-full max-w-(--page-max-width) px-(--page-padding-inline)">
|
<div class="relative z-10 mx-auto w-full max-w-(--page-max-width) px-(--page-padding-inline)">
|
||||||
<div class="relative z-10 w-full max-w-3xl">
|
<div class="relative z-10 w-full max-w-3xl">
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<script>
|
<script>
|
||||||
import { Section, Stack, Container } from "$components";
|
import { Section, Stack, Image } from "$components";
|
||||||
import { onMount, onDestroy } from "svelte";
|
import { onMount, onDestroy } from "svelte";
|
||||||
import yaml from "js-yaml";
|
import yaml from "js-yaml";
|
||||||
import { currentLang, getLangText, createPageTextStore } from "$lib";
|
import { currentLang, getLangText, createPageTextStore } from "$lib";
|
||||||
@@ -22,6 +22,10 @@
|
|||||||
return getLangText(workshop, field, $currentLang);
|
return getLangText(workshop, field, $currentLang);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getGallery(workshop) {
|
||||||
|
return Array.isArray(workshop?.gallery) ? workshop.gallery : [];
|
||||||
|
}
|
||||||
|
|
||||||
function getGalleryClass(count) {
|
function getGalleryClass(count) {
|
||||||
const classes = {
|
const classes = {
|
||||||
1: "grid-cols-1 grid-rows-1",
|
1: "grid-cols-1 grid-rows-1",
|
||||||
@@ -29,7 +33,7 @@
|
|||||||
3: "grid-cols-2 grid-rows-2",
|
3: "grid-cols-2 grid-rows-2",
|
||||||
4: "grid-cols-2 grid-rows-2",
|
4: "grid-cols-2 grid-rows-2",
|
||||||
};
|
};
|
||||||
return classes[count] || classes[1];
|
return classes[count] || "grid-cols-2";
|
||||||
}
|
}
|
||||||
|
|
||||||
function getImageHeightClass(count, index) {
|
function getImageHeightClass(count, index) {
|
||||||
@@ -39,13 +43,13 @@
|
|||||||
return index === 0 ? "h-72 row-span-2" : "h-32";
|
return index === 0 ? "h-72 row-span-2" : "h-32";
|
||||||
}
|
}
|
||||||
if (count === 4) return "h-32";
|
if (count === 4) return "h-32";
|
||||||
return "h-40";
|
return "h-32";
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="safe-area-navbar">
|
<div class="safe-area-navbar">
|
||||||
<!-- Hero Section -->
|
<!-- Hero Section -->
|
||||||
<Section bg="bg-black" class="text-white">
|
<Section bg="bg-gray-900" class="text-white">
|
||||||
<div class="max-w-2xl">
|
<div class="max-w-2xl">
|
||||||
<p class="font-dm-mono text-xs uppercase tracking-widest text-orange-500 mb-4">
|
<p class="font-dm-mono text-xs uppercase tracking-widest text-orange-500 mb-4">
|
||||||
{$text["hero"]?.label}
|
{$text["hero"]?.label}
|
||||||
@@ -66,29 +70,23 @@
|
|||||||
<Section>
|
<Section>
|
||||||
<Stack gap="lg">
|
<Stack gap="lg">
|
||||||
{#each workshops as workshop, index (index)}
|
{#each workshops as workshop, index (index)}
|
||||||
<div class={`grid grid-cols-1 md:grid-cols-2 gap-14 md:gap-16 py-16 border-b border-gray-100 ${index === workshops.length - 1 ? "border-b-0" : ""}`}>
|
<div class={`grid grid-cols-1 md:grid-cols-2 gap-14 md:gap-16 py-16 border-b border-orange-500 ${index === workshops.length - 1 ? "border-b-0" : ""}`}>
|
||||||
<!-- Gallery -->
|
<!-- Gallery -->
|
||||||
<div
|
<div
|
||||||
class={`grid gap-1.5 ${index % 2 === 1 ? "md:order-2" : ""}`}
|
class={`grid gap-1.5 ${index % 2 === 1 ? "md:order-2" : ""}`}
|
||||||
>
|
>
|
||||||
<div class={`grid ${getGalleryClass(workshop.gallery)} gap-1.5`}>
|
<div class={`grid ${getGalleryClass(getGallery(workshop).length)} gap-1.5`}>
|
||||||
{#each Array(workshop.gallery) as _, i}
|
{#each getGallery(workshop) as filename, i (filename)}
|
||||||
<div
|
<div
|
||||||
class={`bg-gray-100 overflow-hidden flex items-center justify-center ${getImageHeightClass(workshop.gallery, i)}`}
|
class={`overflow-hidden ${getImageHeightClass(getGallery(workshop).length, i)}`}
|
||||||
>
|
>
|
||||||
<svg
|
<Image
|
||||||
width="48"
|
src={`/assets/workshops/${filename}`}
|
||||||
height="48"
|
alt={`${getField(workshop, "title")} ${i + 1}`}
|
||||||
viewBox="0 0 24 24"
|
objectFit="cover"
|
||||||
fill="none"
|
class="h-full w-full"
|
||||||
stroke="currentColor"
|
pictureClass="h-full w-full"
|
||||||
stroke-width="1"
|
/>
|
||||||
class="text-gray-300"
|
|
||||||
>
|
|
||||||
<rect x="3" y="3" width="18" height="18" rx="1" />
|
|
||||||
<circle cx="8.5" cy="8.5" r="1.5" />
|
|
||||||
<polyline points="21 15 16 10 5 21" />
|
|
||||||
</svg>
|
|
||||||
</div>
|
</div>
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,17 +1,17 @@
|
|||||||
// ADD PAGES HERE TO REGISTER ROUTES
|
// ADD PAGES HERE TO REGISTER ROUTES
|
||||||
// examples: '/page', '/page/:id'
|
// examples: '/page', '/page/:id'
|
||||||
|
|
||||||
import Home from './Home.svelte'
|
const loadHome = () => import('./Home.svelte');
|
||||||
import Student from './Student.svelte'
|
const loadStudent = () => import('./Student.svelte');
|
||||||
import Mentors from './Mentors.svelte'
|
const loadMentors = () => import('./Mentors.svelte');
|
||||||
import Helpdesk from './Helpdesk.svelte'
|
const loadHelpdesk = () => import('./Helpdesk.svelte');
|
||||||
import OurWork from './OurWork.svelte'
|
const loadOurWork = () => import('./OurWork.svelte');
|
||||||
import AboutUs from './AboutUs.svelte'
|
const loadAboutUs = () => import('./AboutUs.svelte');
|
||||||
import Contact from './Contact.svelte'
|
const loadContact = () => import('./Contact.svelte');
|
||||||
import Managment from './Managment.svelte'
|
const loadManagment = () => import('./Managment.svelte');
|
||||||
import Striim from './Striim.svelte'
|
const loadStriim = () => import('./Striim.svelte');
|
||||||
import Calendar from './Calendar.svelte'
|
const loadCalendar = () => import('./Calendar.svelte');
|
||||||
import Workshops from './Workshops.svelte';
|
const loadWorkshops = () => import('./Workshops.svelte');
|
||||||
|
|
||||||
// Route mapping for language switching
|
// Route mapping for language switching
|
||||||
export const routeMap = {
|
export const routeMap = {
|
||||||
@@ -91,23 +91,23 @@ export function getTranslatedRoute(currentPath, targetLang) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const routes = {
|
export const routes = {
|
||||||
'/': Home,
|
'/': loadHome,
|
||||||
'/tudengile': Student,
|
'/tudengile': loadStudent,
|
||||||
'/student': Student,
|
'/student': loadStudent,
|
||||||
'/mentorid': Mentors,
|
'/mentorid': loadMentors,
|
||||||
'/mentors': Mentors,
|
'/mentors': loadMentors,
|
||||||
'/helpdesk': Helpdesk,
|
'/helpdesk': loadHelpdesk,
|
||||||
'/ettevottele': OurWork,
|
'/ettevottele': loadOurWork,
|
||||||
'/ourwork': OurWork,
|
'/ourwork': loadOurWork,
|
||||||
'/lapikutest': AboutUs,
|
'/lapikutest': loadAboutUs,
|
||||||
'/aboutus': AboutUs,
|
'/aboutus': loadAboutUs,
|
||||||
'/kontakt': Contact,
|
'/kontakt': loadContact,
|
||||||
'/contact': Contact,
|
'/contact': loadContact,
|
||||||
'/juhatus': Managment,
|
'/juhatus': loadManagment,
|
||||||
'/management': Managment,
|
'/management': loadManagment,
|
||||||
'/koolitused': Workshops,
|
'/koolitused': loadWorkshops,
|
||||||
'/workshops': Workshops,
|
'/workshops': loadWorkshops,
|
||||||
'/striim': Striim,
|
'/striim': loadStriim,
|
||||||
'/kalender': Calendar,
|
'/kalender': loadCalendar,
|
||||||
'/calendar': Calendar,
|
'/calendar': loadCalendar,
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user