mirror of
https://github.com/Lapikud/lapikud.github.io.git
synced 2026-08-08 23:09:14 +00:00
updated image optimisation, updated readme accrodingly. fixed image issues, fixed navbar on mobile view + more
This commit is contained in:
@@ -16,6 +16,7 @@
|
||||
import Bot from "lucide-svelte/icons/bot";
|
||||
import HandHeart from "lucide-svelte/icons/hand-heart";
|
||||
import Trophy from "lucide-svelte/icons/trophy";
|
||||
import ExternalLink from "lucide-svelte/icons/external-link";
|
||||
import Menu from "lucide-svelte/icons/menu";
|
||||
import X from "lucide-svelte/icons/x";
|
||||
|
||||
@@ -44,6 +45,7 @@
|
||||
return paths.includes(currentPath);
|
||||
}
|
||||
|
||||
// combines styles for easier modifying
|
||||
function navButtonClass(active = false) {
|
||||
return active
|
||||
? "border-transparent text-orange-500 hover:bg-white/5"
|
||||
@@ -56,18 +58,14 @@
|
||||
: "border-transparent text-white/75 hover:bg-white/5";
|
||||
}
|
||||
|
||||
function languageButtonClass() {
|
||||
return "ml-3 border-transparent bg-transparent px-3 py-1 text-sm text-white/75 hover:bg-white/5";
|
||||
}
|
||||
|
||||
function dropdownItemClass(active = false) {
|
||||
return active
|
||||
? "w-full justify-start border-transparent bg-transparent text-orange-500"
|
||||
: "w-full justify-start border-transparent bg-transparent text-black hover:text-orange-500";
|
||||
? "w-full justify-start gap-2 border-transparent text-orange-500"
|
||||
: "w-full justify-start gap-2 border-transparent text-black hover:text-orange-500";
|
||||
}
|
||||
|
||||
function dropdownPanelClass() {
|
||||
return "nav-dropdown-panel min-w-52 border border-black/10 bg-white/95 p-1 text-black backdrop-blur-md";
|
||||
return "nav-dropdown-panel min-w-52 border border-black/10 bg-white p-1 text-black";
|
||||
}
|
||||
|
||||
// Update currentPath when navigation occurs
|
||||
@@ -173,15 +171,15 @@
|
||||
</style>
|
||||
|
||||
<header class="fixed inset-x-0 top-0 z-50 text-white">
|
||||
<div class="border-b-2 border-orange-500 bg-gray-900 backdrop-blur-md">
|
||||
<div class="border-b-2 border-orange-500 bg-gray-900">
|
||||
<Container class="py-0">
|
||||
{#if $text && Object.keys($text).length > 0}
|
||||
<div class="flex min-h-16 items-center gap-4">
|
||||
<div class="flex h-(--navbar-offset) items-center gap-4">
|
||||
<Button
|
||||
onClick={() => handleNavigation("/")}
|
||||
class="nav-logo-button border-transparent bg-transparent px-0 py-3"
|
||||
class="nav-logo-button border-transparent px-0 py-1 md: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-14 w-auto" />
|
||||
</Button>
|
||||
|
||||
<div class="ml-auto flex w-full items-center justify-end md:w-auto">
|
||||
@@ -232,19 +230,31 @@
|
||||
class={dropdownItemClass(false)}
|
||||
onClick={() => handleNavigation("https://tipilan.ee/", { external: true })}
|
||||
>
|
||||
<Gamepad2 />{$text.eventsPages.tipilan}
|
||||
<Gamepad2 />
|
||||
<span class="flex w-full items-center justify-between gap-4">
|
||||
{$text.eventsPages.tipilan}
|
||||
<ExternalLink size={16} aria-hidden="true" />
|
||||
</span>
|
||||
</Button>
|
||||
<Button
|
||||
class={dropdownItemClass(false)}
|
||||
onClick={() => handleNavigation("https://asikarikas.ee/", { external: true })}
|
||||
>
|
||||
<Trophy />ASI Karikas
|
||||
<Trophy />
|
||||
<span class="flex w-full items-center justify-between gap-4">
|
||||
ASI Karikas
|
||||
<ExternalLink size={16} aria-hidden="true" />
|
||||
</span>
|
||||
</Button>
|
||||
<Button
|
||||
class={dropdownItemClass(false)}
|
||||
onClick={() => handleNavigation("https://remondikohvik.lapikud.ee/", { external: true })}
|
||||
>
|
||||
<Coffee />{$text.eventsPages.repair}
|
||||
<Coffee />
|
||||
<span class="flex w-full items-center justify-between gap-4">
|
||||
{$text.eventsPages.repair}
|
||||
<ExternalLink size={16} aria-hidden="true" />
|
||||
</span>
|
||||
</Button>
|
||||
</Dropdown>
|
||||
|
||||
@@ -289,7 +299,7 @@
|
||||
<!-- Full Screen Mobile Menu -->
|
||||
{#if mobileMenuOpen}
|
||||
<div
|
||||
class="mobile-menu-overlay fixed inset-0 z-100 bg-black/95 mobile-only overflow-y-auto"
|
||||
class="mobile-menu-overlay fixed inset-0 z-100 bg-gray-900 mobile-only overflow-y-auto"
|
||||
data-animation
|
||||
class:menu-opening={!isClosing}
|
||||
class:menu-closing={isClosing}
|
||||
@@ -298,7 +308,7 @@
|
||||
<div class="absolute top-6 right-4">
|
||||
<Button
|
||||
onClick={closeMobileMenu}
|
||||
class="ml-auto p-2 transition-colors flex bg-transparent hover:text-orange-500 border-transparent"
|
||||
class="ml-auto p-2 transition-colors flex bg-transparent border-transparent"
|
||||
aria-label="Close menu"
|
||||
>
|
||||
<X size={32} color="white" />
|
||||
@@ -309,7 +319,7 @@
|
||||
<Stack gap="gap-5" class="flex-1 px-8 pt-24 pb-12 text-white text-xl">
|
||||
<Button
|
||||
onClick={() => handleNavigation("/")}
|
||||
class="mb-3 border-transparent bg-transparent px-0 py-0 text-left text-white hover:text-white"
|
||||
class="mb-3 border-transparent bg-transparent px-0 py-0 text-left"
|
||||
>
|
||||
<img src="/assets/logo.svg" alt="Lapikud Logo" class="h-12 w-auto" />
|
||||
</Button>
|
||||
@@ -320,27 +330,27 @@
|
||||
<Stack gap="gap-2" class="pl-4">
|
||||
<Button
|
||||
onClick={() => handleNavigation("/lapikutest")}
|
||||
class={`text-left transition-colors flex items-center gap-2 bg-transparent border-transparent ${isCurrent("/lapikutest") || isCurrent("/aboutus") ? "text-orange-500" : "hover:text-orange-500"}`}
|
||||
class={`text-left transition-colors flex items-center gap-2 bg-transparent border-transparent ${isCurrent("/lapikutest") || isCurrent("/aboutus") ? "text-orange-500" : ""}`}
|
||||
>
|
||||
{$text.aboutPages.info}
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => handleNavigation("/tudengile")}
|
||||
class={`text-left transition-colors flex items-center gap-2 bg-transparent border-transparent ${isCurrent("/tudengile") || isCurrent("/student") ? "text-orange-500" : "hover:text-orange-500"}`}
|
||||
class={`text-left transition-colors flex items-center gap-2 bg-transparent border-transparent ${isCurrent("/tudengile") || isCurrent("/student") ? "text-orange-500" : ""}`}
|
||||
>
|
||||
<Bot size={20} />
|
||||
{$text.aboutPages.join}
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => handleNavigation("/mentorid")}
|
||||
class={`text-left transition-colors flex items-center gap-2 bg-transparent border-transparent ${isCurrent("/mentorid") || isCurrent("/mentors") ? "text-orange-500" : "hover:text-orange-500"}`}
|
||||
class={`text-left transition-colors flex items-center gap-2 bg-transparent border-transparent ${isCurrent("/mentorid") || isCurrent("/mentors") ? "text-orange-500" : ""}`}
|
||||
>
|
||||
<HandHeart size={20} />
|
||||
{$text.aboutPages.mentors}
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => handleNavigation("/juhatus")}
|
||||
class={`text-left transition-colors flex items-center gap-2 bg-transparent border-transparent ${isCurrent("/juhatus") || isCurrent("/management") ? "text-orange-500" : "hover:text-orange-500"}`}
|
||||
class={`text-left transition-colors flex items-center gap-2 bg-transparent border-transparent ${isCurrent("/juhatus") || isCurrent("/management") ? "text-orange-500" : ""}`}
|
||||
>
|
||||
<Lectern size={20} />
|
||||
{$text.aboutPages.board}
|
||||
@@ -354,31 +364,40 @@
|
||||
<Stack gap="gap-2" class="pl-4">
|
||||
<Button
|
||||
onClick={() => handleNavigation("/koolitused")}
|
||||
class="text-left transition-colors flex items-center gap-2 bg-transparent hover:text-orange-500 border-transparent"
|
||||
class="text-left transition-colors flex items-center gap-2 bg-transparent border-transparent ${isCurrent("/koolitused") || isCurrent("/workshops") ? "text-orange-500" : ""}"
|
||||
>
|
||||
<Presentation size={20} />
|
||||
{$text.eventsPages.workshops}
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => handleNavigation("https://tipilan.ee/", { external: true })}
|
||||
class="text-left transition-colors flex items-center gap-2 bg-transparent hover:text-orange-500 border-transparent"
|
||||
class="text-left transition-colors flex items-center gap-2 bg-transparent border-transparent"
|
||||
>
|
||||
<Gamepad2 size={20} />
|
||||
{$text.eventsPages.tipilan}
|
||||
<span class="flex w-full items-center justify-between gap-4">
|
||||
{$text.eventsPages.tipilan}
|
||||
<ExternalLink size={16} aria-hidden="true" />
|
||||
</span>
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => handleNavigation("https://asikarikas.ee/", { external: true })}
|
||||
class="text-left transition-colors flex items-center gap-2 bg-transparent hover:text-orange-500 border-transparent"
|
||||
class="text-left transition-colors flex items-center gap-2 bg-transparent border-transparent"
|
||||
>
|
||||
<Trophy size={20} />
|
||||
ASI Karikas
|
||||
<span class="flex w-full items-center justify-between gap-4">
|
||||
ASI Karikas
|
||||
<ExternalLink size={16} aria-hidden="true" />
|
||||
</span>
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => handleNavigation("https://remondikohvik.lapikud.ee/", { external: true })}
|
||||
class="text-left transition-colors flex items-center gap-2 bg-transparent hover:text-orange-500 border-transparent"
|
||||
class="text-left transition-colors flex items-center gap-2 bg-transparent border-transparent"
|
||||
>
|
||||
<Coffee size={20} />
|
||||
{$text.eventsPages.repair}
|
||||
<span class="flex w-full items-center justify-between gap-4">
|
||||
{$text.eventsPages.repair}
|
||||
<ExternalLink size={16} aria-hidden="true" />
|
||||
</span>
|
||||
</Button>
|
||||
</Stack>
|
||||
</Stack>
|
||||
@@ -387,13 +406,13 @@
|
||||
<Stack gap="gap-3">
|
||||
<Button
|
||||
onClick={() => handleNavigation("/helpdesk")}
|
||||
class={`text-left text-2xl font-semibold transition-colors bg-transparent border-transparent ${isCurrent("/helpdesk") ? "text-orange-500" : "hover:text-orange-500"}`}
|
||||
class={`text-left text-2xl font-semibold transition-colors bg-transparent border-transparent ${isCurrent("/helpdesk") ? "text-orange-500" : ""}`}
|
||||
>
|
||||
{$text.helpdesk}
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => handleNavigation("/kontakt")}
|
||||
class={`text-left text-2xl font-semibold transition-colors bg-transparent border-transparent ${isCurrent("/kontakt") || isCurrent("/contact") ? "text-orange-500" : "hover:text-orange-500"}`}
|
||||
class={`text-left text-2xl font-semibold transition-colors bg-transparent border-transparent ${isCurrent("/kontakt") || isCurrent("/contact") ? "text-orange-500" : ""}`}
|
||||
>
|
||||
{$text.contact}
|
||||
</Button>
|
||||
|
||||
125
src/lib/imageHelpers.js
Normal file
125
src/lib/imageHelpers.js
Normal file
@@ -0,0 +1,125 @@
|
||||
/**
|
||||
* Image path helpers for assets under /public/assets.
|
||||
*
|
||||
* Optimised convention:
|
||||
* /assets/{category}/optimised/{imageName}/
|
||||
* full.webp, full.jpg
|
||||
* 400.webp, 800.webp, 1200.webp (optional responsive variants)
|
||||
*/
|
||||
|
||||
/**
|
||||
* Get the image name without extension
|
||||
* @param {string} filename - e.g., "mari.jpg"
|
||||
* @returns {string} - e.g., "mari"
|
||||
*/
|
||||
function getImageName(filename) {
|
||||
if (!filename) return "";
|
||||
const lastDot = filename.lastIndexOf(".");
|
||||
return lastDot > 0 ? filename.slice(0, lastDot) : filename;
|
||||
}
|
||||
|
||||
function normaliseFormat(format) {
|
||||
return String(format || "").toLowerCase() === "webp" ? "webp" : "jpg";
|
||||
}
|
||||
|
||||
function getOptimisedBasePath(category, filename) {
|
||||
const imageName = getImageName(filename);
|
||||
return `/assets/${category}/optimised/${imageName}`;
|
||||
}
|
||||
|
||||
function getVariantSrc(category, filename, variant = "full", format = "webp") {
|
||||
const ext = normaliseFormat(format);
|
||||
const safeVariant = String(variant || "full").trim() || "full";
|
||||
return `${getOptimisedBasePath(category, filename)}/${safeVariant}.${ext}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an asset path from category root (e.g. SVG icons).
|
||||
* @param {string} category - e.g., "helpdesk-page-images"
|
||||
* @param {string} filename - e.g., "helpdesk-icon.svg" or "helpdesk-icon"
|
||||
* @param {string} defaultExt - e.g., "svg"
|
||||
* @returns {string} - e.g., "/assets/helpdesk-page-images/helpdesk-icon.svg"
|
||||
*/
|
||||
export function getRootAssetPath(category, filename, defaultExt = "svg") {
|
||||
const value = String(filename || "").trim();
|
||||
if (!value) return "";
|
||||
const hasExt = /\.[^./\\]+$/.test(value);
|
||||
const fileWithExt = hasExt ? value : `${value}.${defaultExt}`;
|
||||
return `/assets/${category}/${fileWithExt}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the optimised image path.
|
||||
* @param {string} category - e.g., "mentors-images", "past-management-images"
|
||||
* @param {string} filename - e.g., "mari.jpg"
|
||||
* @param {string} format - "webp" or "jpg" (default: "webp")
|
||||
* @param {string} variant - e.g., "full", "400", "800", "1200" (default: "full")
|
||||
* @returns {string} - e.g., "/assets/mentors-images/optimised/mari/full.webp"
|
||||
*/
|
||||
export function getOptimisedImagePath(category, filename, format = "webp", variant = "full") {
|
||||
return getVariantSrc(category, filename, variant, format);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get srcset for responsive variants.
|
||||
* @param {string} category - e.g., "home-page-images"
|
||||
* @param {string} filename - e.g., "hero.png"
|
||||
* @param {number[]} widths - e.g., [400, 800, 1200]
|
||||
* @param {string} format - "webp" or "jpg"
|
||||
* @returns {string} - srcset string with width descriptors
|
||||
*/
|
||||
export function getResponsiveImageSrcSet(category, filename, widths = [400, 800, 1200], format = "webp") {
|
||||
return widths
|
||||
.map((w) => `${getVariantSrc(category, filename, w, format)} ${w}w`)
|
||||
.join(", ");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get srcset for hero/background images with responsive variants
|
||||
* @param {string} category - e.g., "home-page-images"
|
||||
* @param {string} filename - e.g., "hero.png"
|
||||
* @returns {string} - srcset string with 400w, 800w, 1200w variants
|
||||
*/
|
||||
export function getHeroImageSrcSet(category, filename) {
|
||||
return getResponsiveImageSrcSet(category, filename, [400, 800, 1200], "webp");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the full size image path for hero images
|
||||
* @param {string} category - e.g., "home-page-images"
|
||||
* @param {string} filename - e.g., "hero.png"
|
||||
* @returns {string} - e.g., "/assets/home-page-images/optimised/hero/1200.webp"
|
||||
*/
|
||||
export function getHeroImagePath(category, filename) {
|
||||
return getOptimisedImagePath(category, filename, "webp", "1200");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the fallback (jpg) path for hero images.
|
||||
* @param {string} category - e.g., "home-page-images"
|
||||
* @param {string} filename - e.g., "hero.png"
|
||||
* @returns {string} - e.g., "/assets/home-page-images/optimised/hero/full.jpg"
|
||||
*/
|
||||
export function getHeroImageFallback(category, filename) {
|
||||
return getOptimisedImagePath(category, filename, "jpg");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get srcset for regular optimised images (full variant only)
|
||||
* @param {string} category - e.g., "mentors-images"
|
||||
* @param {string} filename - e.g., "mari.jpg"
|
||||
* @returns {string} - srcset string with webp and jpg variants
|
||||
*/
|
||||
export function getOptimisedImageSrcSet(category, filename) {
|
||||
return getOptimisedImagePath(category, filename, "webp");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the fallback jpg for regular optimised images
|
||||
* @param {string} category - e.g., "mentors-images"
|
||||
* @param {string} filename - e.g., "mari.jpg"
|
||||
* @returns {string} - e.g., "/assets/mentors-images/optimised/mari/full.jpg"
|
||||
*/
|
||||
export function getOptimisedImageFallback(category, filename) {
|
||||
return getOptimisedImagePath(category, filename, "jpg");
|
||||
}
|
||||
@@ -5,6 +5,7 @@
|
||||
} from "$components";
|
||||
import { onMount, onDestroy } from "svelte";
|
||||
import { createPageTextStore } from "$lib";
|
||||
import { getHeroImageFallback, getHeroImageSrcSet, getRootAssetPath } from "$lib/imageHelpers.js";
|
||||
import yaml from 'js-yaml';
|
||||
|
||||
import { MapLibre, Marker } from "svelte-maplibre";
|
||||
@@ -13,8 +14,9 @@
|
||||
import { navigate } from "$lib/router/router.js";
|
||||
|
||||
// Images
|
||||
const helpdeskbg = "/assets/helpdesk/helpdesk_bg.jpg";
|
||||
const helpdesk = "/assets/helpdesk/helpdesk-on-black.png";
|
||||
const helpdeskbg = getHeroImageFallback("helpdesk-page-images", "helpdesk_bg");
|
||||
const helpdeskbgWebpSet = getHeroImageSrcSet("helpdesk-page-images", "helpdesk_bg");
|
||||
const helpdesk = getRootAssetPath("helpdesk-page-images", "helpdesk-on-black.png");
|
||||
|
||||
// Coordinates for Akadeemia tee 5, 12616 Tallinn [longitude, latitude]
|
||||
const center = [24.66887400515207, 59.396427975093935];
|
||||
@@ -24,13 +26,17 @@
|
||||
const tehnikaBusStop = [24.67328945396854, 59.39508874042947];
|
||||
|
||||
// Google Maps directions URL
|
||||
const getDirectionsUrl = (lng, lat) => {
|
||||
const getDirectionsUrl = (lng, lat, loc) => {
|
||||
if (loc == "bus"){
|
||||
return `https://www.google.com/maps/dir/?api=1&destination=${lat},${lng}&travelmode=transit`;
|
||||
}
|
||||
|
||||
return `https://www.google.com/maps/dir/?api=1&destination=${lat},${lng}`;
|
||||
};
|
||||
|
||||
// Navigate to directions using router
|
||||
const openDirections = (lng, lat) => {
|
||||
navigate(getDirectionsUrl(lng, lat), { external: true });
|
||||
const openDirections = (lng, lat, loc) => {
|
||||
navigate(getDirectionsUrl(lng, lat, loc), { external: true });
|
||||
};
|
||||
|
||||
// Load pricing data
|
||||
@@ -54,11 +60,14 @@
|
||||
<div class="flex absolute justify-center items-center flex-row w-full h-[42vh] px-4">
|
||||
<img src={helpdesk} alt={$text["hero"]?.title || "Helpdesk logo"} class="z-10" />
|
||||
</div>
|
||||
<img
|
||||
class="h-[42vh] object-cover object-left w-full opacity-50 blur-[1.5px]"
|
||||
src={helpdeskbg}
|
||||
alt="Helpdesk"
|
||||
/>
|
||||
<picture>
|
||||
<source srcset={helpdeskbgWebpSet} type="image/webp" />
|
||||
<img
|
||||
class="h-[42vh] object-cover object-left w-full opacity-50 blur-[1.5px]"
|
||||
src={helpdeskbg}
|
||||
alt="Helpdesk"
|
||||
/>
|
||||
</picture>
|
||||
</div>
|
||||
|
||||
<!-- Main Content -->
|
||||
@@ -159,8 +168,8 @@
|
||||
<div
|
||||
role="button"
|
||||
tabindex="0"
|
||||
onclick={() => openDirections(keemiaBusStop[0], keemiaBusStop[1])}
|
||||
onkeydown={(e) => (e.key === 'Enter' || e.key === ' ') && openDirections(keemiaBusStop[0], keemiaBusStop[1])}
|
||||
onclick={() => openDirections(keemiaBusStop[0], keemiaBusStop[1], "bus")}
|
||||
onkeydown={(e) => (e.key === 'Enter' || e.key === ' ') && openDirections(keemiaBusStop[0], keemiaBusStop[1], "bus")}
|
||||
class="flex flex-col items-center hover:scale-110 transition-transform cursor-pointer"
|
||||
title="Get directions to Keemia bus stop"
|
||||
>
|
||||
@@ -174,8 +183,8 @@
|
||||
<div
|
||||
role="button"
|
||||
tabindex="0"
|
||||
onclick={() => openDirections(tehnikaBusStop[0], tehnikaBusStop[1])}
|
||||
onkeydown={(e) => (e.key === 'Enter' || e.key === ' ') && openDirections(tehnikaBusStop[0], tehnikaBusStop[1])}
|
||||
onclick={() => openDirections(tehnikaBusStop[0], tehnikaBusStop[1], "bus")}
|
||||
onkeydown={(e) => (e.key === 'Enter' || e.key === ' ') && openDirections(tehnikaBusStop[0], tehnikaBusStop[1], "bus")}
|
||||
class="flex flex-col items-center hover:scale-110 transition-transform cursor-pointer"
|
||||
title="Get directions to Tehnikaülikool bus stop"
|
||||
>
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import {Section, Stack, Grid, Center, Image, Svg, Button} from "$components";
|
||||
import { navigate } from "$lib/router/router.js";
|
||||
import { createPageTextStore, currentLang } from "$lib";
|
||||
import { getHeroImagePath, getHeroImageSrcSet, getHeroImageFallback, getOptimisedImagePath, getOptimisedImageFallback } from "$lib/imageHelpers.js";
|
||||
import { onMount, onDestroy } from "svelte";
|
||||
import yaml from 'js-yaml';
|
||||
|
||||
@@ -21,6 +22,19 @@
|
||||
onDestroy(() => {
|
||||
text.destroy();
|
||||
});
|
||||
|
||||
const partnerLogoExtByName = {
|
||||
taltech_logo: "jpg",
|
||||
eas: "svg",
|
||||
hextech_logo: "svg",
|
||||
};
|
||||
|
||||
function getPartnerLogoPath(name) {
|
||||
if (!name) return "";
|
||||
if (name.includes(".")) return `/assets/partners/${name}`;
|
||||
const ext = partnerLogoExtByName[name] || "png";
|
||||
return `/assets/partners/${name}.${ext}`;
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- Hero Section -->
|
||||
@@ -28,7 +42,9 @@
|
||||
<div class="relative min-h-screen flex items-center">
|
||||
<div class="absolute inset-0 left-1/2 w-screen -translate-x-1/2" aria-hidden="true">
|
||||
<Image
|
||||
src="/assets/home-page-images/hero.jpg"
|
||||
webpSrc={getHeroImagePath("home-page-images", "hero")}
|
||||
src={getHeroImageFallback("home-page-images", "hero")}
|
||||
webpSrcSet={getHeroImageSrcSet("home-page-images", "hero")}
|
||||
alt={$text["hero"]?.imageAlt || "illustrative hero image"}
|
||||
objectFit="cover"
|
||||
class="w-full h-full"
|
||||
@@ -88,8 +104,8 @@
|
||||
<Center dir="col" class="w-full max-w-sm justify-start mx-auto">
|
||||
<div class="w-[min(72vw,240px)] md:w-[min(26vw,280px)] lg:w-[min(23vw,300px)] aspect-square rounded-full overflow-hidden">
|
||||
<Image
|
||||
src="/assets/student-images/image_1.jpg"
|
||||
webpSrc=""
|
||||
webpSrc={getOptimisedImagePath("home-page-images", "student_temp", "webp")}
|
||||
src={getOptimisedImageFallback("home-page-images", "student_temp")}
|
||||
alt={$text["services"]?.student?.imageAlt || "Tudengile Pilt"}
|
||||
objectFit="cover"
|
||||
class="w-full h-full rounded-full object-[center_35%]"
|
||||
@@ -103,8 +119,8 @@
|
||||
<Center dir="col" class="w-full max-w-sm justify-start mx-auto">
|
||||
<div class="w-[min(72vw,240px)] md:w-[min(26vw,280px)] lg:w-[min(23vw,300px)] aspect-square rounded-full overflow-hidden">
|
||||
<Image
|
||||
src="/assets/helpdesk/helpdesk_bg.jpg"
|
||||
webpSrc=""
|
||||
webpSrc={getOptimisedImagePath("home-page-images", "helpdesk", "webp")}
|
||||
src={getOptimisedImageFallback("home-page-images", "helpdesk")}
|
||||
alt={$text["services"]?.helpdesk?.imageAlt || "Helpdesk Pilt"}
|
||||
objectFit="cover"
|
||||
class="w-full h-full rounded-full object-[center_38%]"
|
||||
@@ -118,8 +134,8 @@
|
||||
<Center dir="col" class="w-full max-w-sm justify-start mx-auto md:col-span-2 lg:col-span-1">
|
||||
<div class="w-[min(72vw,240px)] md:w-[min(26vw,280px)] lg:w-[min(23vw,300px)] aspect-square rounded-full overflow-hidden">
|
||||
<Image
|
||||
src="/assets/asi-karikas-2023-images/miina_harma.jpeg"
|
||||
webpSrc=""
|
||||
webpSrc={getOptimisedImagePath("home-page-images", "company_temp", "webp")}
|
||||
src={getOptimisedImageFallback("home-page-images", "company_temp")}
|
||||
alt={$text["services"]?.company?.imageAlt || "Ettevõttele Pilt"}
|
||||
objectFit="cover"
|
||||
class="w-full h-full rounded-full object-[center_25%]"
|
||||
@@ -185,7 +201,8 @@
|
||||
<div class="relative overflow-hidden min-h-90 md:h-full md:self-stretch">
|
||||
<div class="absolute inset-0">
|
||||
<Image
|
||||
src="/assets/home-page-images/about_us.png"
|
||||
webpSrc={getOptimisedImagePath("home-page-images", "about_us", "webp")}
|
||||
src={getOptimisedImageFallback("home-page-images", "about_us")}
|
||||
alt={$text["aboutSection"]?.imageAlt || "Lapikud team"}
|
||||
objectFit="cover"
|
||||
class="h-full w-full max-w-none!"
|
||||
@@ -284,7 +301,8 @@
|
||||
<div class="relative overflow-hidden min-h-90 md:min-h-0 md:h-full md:self-stretch">
|
||||
<div class="absolute inset-0">
|
||||
<Image
|
||||
src="/assets/home-page-images/temp.png"
|
||||
webpSrc={getOptimisedImagePath("home-page-images", "temp", "webp")}
|
||||
src={getOptimisedImageFallback("home-page-images", "temp")}
|
||||
alt={$text["whatWeDo"]?.imageAlt || "Lapikud parandamas riistvara"}
|
||||
objectFit="cover"
|
||||
class="h-full w-full max-w-none!"
|
||||
@@ -349,11 +367,10 @@
|
||||
title={partner.name}
|
||||
>
|
||||
{#if partner.image}
|
||||
<Image
|
||||
src={`/assets/partners/${partner.image}`}
|
||||
<img
|
||||
src={getPartnerLogoPath(partner.image)}
|
||||
alt={partner.name}
|
||||
objectFit="contain"
|
||||
class="w-auto max-h-[150px] transition-all duration-300"
|
||||
class="w-auto max-h-[150px] transition-all duration-300 object-contain"
|
||||
/>
|
||||
{:else}
|
||||
<span class="font-semibold text-center">{partner.name}</span>
|
||||
@@ -362,11 +379,10 @@
|
||||
{:else}
|
||||
<div class="flex h-[170px] items-center justify-center p-3 transition-all duration-300">
|
||||
{#if partner.image}
|
||||
<Image
|
||||
src={`/assets/partners/${partner.image}`}
|
||||
<img
|
||||
src={getPartnerLogoPath(partner.image)}
|
||||
alt={partner.name}
|
||||
objectFit="contain"
|
||||
class="w-auto max-h-[150px] transition-all duration-300"
|
||||
class="w-auto max-h-[150px] transition-all duration-300 object-contain"
|
||||
/>
|
||||
{:else}
|
||||
<span class="font-semibold text-center">{partner.name}</span>
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
import { onMount, onDestroy } from "svelte";
|
||||
import yaml from "js-yaml";
|
||||
import { currentLang, getLangText, createPageTextStore } from "$lib";
|
||||
import { getOptimisedImagePath, getOptimisedImageFallback } from "$lib/imageHelpers.js";
|
||||
import Mail from "lucide-svelte/icons/mail";
|
||||
import Phone from "lucide-svelte/icons/phone";
|
||||
|
||||
@@ -92,11 +93,14 @@
|
||||
{#each currentManagement as member (member.name)}
|
||||
<div class="flex flex-col sm:flex-row gap-6 items-start">
|
||||
<div class="w-full sm:w-auto sm:max-w-[200px] aspect-square shrink-0">
|
||||
<img
|
||||
src={`/assets/management-images/${member.photo}`}
|
||||
alt={member.name}
|
||||
class="w-full h-full object-cover rounded-md"
|
||||
/>
|
||||
<picture>
|
||||
<source srcset={getOptimisedImagePath("management-images", member.photo, "webp")} type="image/webp" />
|
||||
<img
|
||||
src={getOptimisedImageFallback("management-images", member.photo)}
|
||||
alt={member.name}
|
||||
class="w-full h-full object-cover rounded-md"
|
||||
/>
|
||||
</picture>
|
||||
</div>
|
||||
<div class="flex flex-col gap-2 grow">
|
||||
<h3 class="text-2xl font-medium">{member.name}</h3>
|
||||
@@ -153,7 +157,8 @@
|
||||
<div class="h-24 w-24 overflow-hidden rounded-[10px] bg-linear-to-br outline-2 outline-transparent outline-offset-2 transition [@media(min-width:769px)]:h-28 [@media(min-width:769px)]:w-28">
|
||||
{#if member.image}
|
||||
<Image
|
||||
src={`/assets/past-management-images/optimised/${member.image}`}
|
||||
webpSrc={getOptimisedImagePath("past-management-images", member.image, "webp")}
|
||||
src={getOptimisedImageFallback("past-management-images", member.image)}
|
||||
alt={member.name}
|
||||
objectFit="cover"
|
||||
class="block h-full w-full"
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import { onMount, onDestroy } from "svelte";
|
||||
import yaml from "js-yaml";
|
||||
import { currentLang, getLangText, createPageTextStore } from "$lib";
|
||||
import { getOptimisedImagePath, getOptimisedImageFallback } from "$lib/imageHelpers.js";
|
||||
|
||||
import Mail from "lucide-svelte/icons/mail";
|
||||
import Phone from "lucide-svelte/icons/phone";
|
||||
@@ -82,16 +83,14 @@
|
||||
<!-- Mentor Photo -->
|
||||
{#if mentor.photo}
|
||||
<div class="shrink-0">
|
||||
<img
|
||||
src={`/assets/mentors-images/optimised/${mentor.photo}`}
|
||||
alt={mentor.name}
|
||||
class="w-full md:w-48 h-48 object-cover"
|
||||
on:error={(e) => {
|
||||
e.target.src =
|
||||
"/assets/mentors-images/original/" +
|
||||
mentor.photo;
|
||||
}}
|
||||
/>
|
||||
<picture>
|
||||
<source srcset={getOptimisedImagePath("mentors-images", mentor.photo, "webp")} type="image/webp" />
|
||||
<img
|
||||
src={getOptimisedImageFallback("mentors-images", mentor.photo)}
|
||||
alt={mentor.name}
|
||||
class="w-full md:w-48 h-48 object-cover"
|
||||
/>
|
||||
</picture>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import { onMount, onDestroy } from "svelte";
|
||||
import yaml from "js-yaml";
|
||||
import { currentLang, getLangText, createPageTextStore } from "$lib";
|
||||
import { getOptimisedImagePath, getOptimisedImageFallback } from "$lib/imageHelpers.js";
|
||||
|
||||
import ExternalLink from "lucide-svelte/icons/external-link";
|
||||
|
||||
@@ -33,12 +34,12 @@
|
||||
|
||||
function projectImage(project) {
|
||||
return project?.photo
|
||||
? `/assets/ourwork-images/optimised/${project.photo}`
|
||||
? getOptimisedImagePath("ourwork-images", project.photo, "webp")
|
||||
: "";
|
||||
}
|
||||
|
||||
function handleImageError(event, photo) {
|
||||
event.currentTarget.src = `/assets/ourwork-images/original/${photo}`;
|
||||
event.currentTarget.src = getOptimisedImageFallback("ourwork-images", photo);
|
||||
}
|
||||
|
||||
function openCtaEmail() {
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import { onMount, onDestroy } from "svelte";
|
||||
import yaml from "js-yaml";
|
||||
import { currentLang, getLangText, createPageTextStore } from "$lib";
|
||||
import { getRootAssetPath } from "$lib/imageHelpers.js";
|
||||
|
||||
let workshops = [];
|
||||
const text = createPageTextStore("Workshops");
|
||||
@@ -81,7 +82,7 @@
|
||||
class={`overflow-hidden ${getImageHeightClass(getGallery(workshop).length, i)}`}
|
||||
>
|
||||
<Image
|
||||
src={`/assets/workshops/${filename}`}
|
||||
src={getRootAssetPath("workshop-images", filename, "jpg")}
|
||||
alt={`${getField(workshop, "title")} ${i + 1}`}
|
||||
objectFit="cover"
|
||||
class="h-full w-full"
|
||||
|
||||
Reference in New Issue
Block a user