Big bang/webgune berria (#18)

* chore: netlify deployments plugina kentzen du

* chore: Strapi berria instalatzen du

* feat: astro instalatzen du

* chore: cypress instalatzen du

* feat: back eta front konektatzen ditu

* feat: server side rendering egiten du

* chore: Strapin enterprise edition flagak kentzen ditu

* feat: euskara sartzen du

* chore: Strapi bertsioa igotzen du

* feat: preview modua sortzen du ingurune bariableen bidez

* aurreko commitarekin doa

* fix: cypress konpontzen du

* chore: Strapi bertsioa igotzen du

* feat: ekintza back-ean sortzen du

* feat: zikloa eta erakundea sortzen ditu

* feat: erakundeen kudeaketa hobetzen du

* chore: pnpm erabiltzen du

* feat: Strapi bertsioa igotzen du

* ekintza sortzen du eta zerbitzuak eguneratzen ditu

* chore: graphql erabiltzen du populazioa ekiditeko

* chore: collection erabiltzen du ekintzetarako

* chore: bertsioak igotzen ditu

* chore: Astro bertsioa igotzen du

* chore: Strapi bertsioa igotzen du

* feat: home-a hasten du

* feat: zig zag separadorea sortzen du

* feat: zig zaga doitzen du

* feat: heroa doitzen du

* feat: agenda sortzen du

* feat: agenda eta izan labazkide atalak konektatzen ditu

* feat: home-aren edukia sortzen du

* chore: app.js sartzen du WTF

* feat: header-a sortzen du

* feat: card-a hobetzen du

* feat: footer-a sortzen du

* feat: ekintzen zerrenda erakusten du

* feat: zikloaren bista sortzen du

* fix: header-aren estekak konpontzen ditu

* chore: remove unused code

* chore: components directorioa kentzen du

* chore: typescript konfigurazioa hobetzen du

* chore: views path-a sortzen du

* feat: zikloaren deskribapena erakusten du

* feat: ekintzaren bista sortzen du

* feat: zig zag marra erakusten du

* ignore: comment

* feat: hitzordua erakusten du

* feat: zikloa erakusten du

* feat: Waveko erroreak errepasatzen ditu

* feat: Wave birpasatzen du

* feat: zerrendan irudia erakusten du

* feat: karga optimizatzen du

* feat: irudien formatuak kontuan hartzen ditu

* feat: zikloan ekintza irudiak erakusten ditu

* feat: ekintza zerrenda atontzen du

* feat: header-eko estekak markatzen ditu

* feat: irudi estela erakusten du

* feat: responsive-a hobetzen du

* feat: orri basikoa sortzen du

* feat: hasiera responsive egiten du

* feat: overflow konpontzen du

* feat: bug-a uzten du LOL

* feat: irudi estela mugikorrean aktibatzen du

* feat: orri basikoa eta ekintza mugikorrean jartzen ditu

* feat: mugikor menua sortzen du

* feat: mugikor menua amaitzen du

* feat: build estatikoa sortzen du

* feat: sitemap-a sortzen du

* feat: rss feed-a sortzen du

* chore: ingurune aldagaiak sortzen ditu

* chore: public-proxy komandoa sortzen du

* feat: irudiak webp formatuan jartzen ditu

* fix: irudiak erakusten ditu ekintzen zerrendan

* feat: ekintza zerrendaren irudia animatzen du

* refactor: extract functions

* fix: animazioa konpontzen du

* feat: irudi estela hobetzen du

* chore: prerender aldagaiak kentzen ditu

* chore: SASS warning-ak kentzen ditu

* feat: ziklo eta ekintzetan H1 ezartzen du

* feat: slideshow-a sortzen du

* feat: ekintzak hitzorduan oinarrituta filtratzen ditu

* feat: hitzorduen arabera filtratzen du eta moment kentzen du

* feat: titulua aldatzen du

* feat: menua eguneratzen du

* feat: parte hartun deskribapena sortzen du

* feat: Cloudinary konfiguratzen du

* fix: cloudinary konfigurazioa konpontzen du

* feat: erositako fonteak jartzen ditu
This commit is contained in:
Aitor Urrutia
2025-08-29 19:43:56 +02:00
committed by GitHub
parent 67110a4821
commit 93fb0c9b40
325 changed files with 23452 additions and 26547 deletions

View File

@@ -0,0 +1,21 @@
---
import { type EkintzaSnippet } from "models/Ekintza";
import { EkintzaZerrenda as EkintzaZerrendaReact } from "./react/EkintzaZerrenda";
export interface Props {
ekintzak: EkintzaSnippet[];
title?: string;
description?: string;
isMainTitle?: boolean;
}
const { ekintzak, title, description, isMainTitle = false } = Astro.props;
---
<EkintzaZerrendaReact
ekintzak={ekintzak}
title={title}
description={description}
isMainTitle
client:idle
/>

View File

@@ -0,0 +1,140 @@
---
import Logo from "../../assets/labaLogo.svg";
import ZigZag from "./ZigZag.astro";
---
<ZigZag from="gorria-background" to="marroia" />
<footer class="footer">
<div class="lehenengo-lerroa">
<Logo class="footer-logo" />
<address>Gazteluko plaza, 2. 31001 Iruñea, Nafarroa</address>
</div>
<div class="bigarren-lerroa">
<nav>
<ul>
<li class="elementu-legala">
<a href="/lege-oharra">Lege oharra</a>
</li>
<li class="elementu-legala">
<a href="/pribatutasun-politika">Pribatutasun politika</a>
</li>
</ul>
</nav>
<nav>
<ul class="sare-sozialak">
<li><a href="https://twitter.com/labasarea/">X</a></li>
<li><a href="https://www.instagram.com/labasarea/">Instagram</a></li>
</ul>
</nav>
</div>
</footer>
<style lang="scss">
@use "../../style/tools/toDVW.scss";
@use "../../style/tools/mediaQueries.scss";
@use "../../style/mixins/textFonts.scss";
.footer {
padding: var(--space-05-mobile);
background-color: var(--marroia);
color: var(--gorria);
@include mediaQueries.aboveTablet {
padding: var(--space-07-desktop);
}
}
.footer-logo {
height: var(--space-05-mobile);
@include mediaQueries.aboveTablet {
height: var(--space-07-desktop);
}
}
.lehenengo-lerroa {
display: flex;
justify-content: space-between;
align-items: flex-end;
border-bottom: #{toDVW.toMobileDVW(2)} solid var(--gorria);
padding-bottom: var(--space-05-mobile);
@include mediaQueries.aboveTablet {
padding-bottom: var(--space-07-desktop);
border-bottom: #{toDVW.toDesktopDVW(2)} solid var(--gorria);
}
}
.bigarren-lerroa {
display: flex;
flex-direction: column;
padding-top: var(--space-05-mobile);
gap: var(--space-02-mobile);
@include mediaQueries.aboveTablet {
flex-direction: row;
justify-content: space-between;
padding-top: var(--space-07-desktop);
gap: var(--space-02-desktop);
}
}
address {
font-style: normal;
text-transform: uppercase;
text-align: end;
@include textFonts.captionFont;
@include mediaQueries.aboveTablet {
text-align: initial;
@include textFonts.bodyFont;
}
}
ul {
display: flex;
padding: 0;
list-style: none;
}
.elementu-legala {
&:not(:last-child) {
margin-right: var(--space-02-mobile);
&::after {
content: "/";
margin-left: var(--space-02-mobile);
}
}
@include mediaQueries.aboveTablet {
&:not(:last-child) {
margin-right: var(--space-02-desktop);
&::after {
content: "/";
margin-left: var(--space-02-desktop);
}
}
}
}
.sare-sozialak {
gap: var(--space-04-desktop);
text-transform: uppercase;
}
a {
color: var(--gorria);
&:hover {
text-decoration: none;
}
}
</style>

View File

@@ -0,0 +1,281 @@
---
import Logo from "../../assets/labaLogo.svg";
import Hamburger from "../../assets/hamburger.svg";
import Close from "../../assets/close.svg";
import ZigZagMarra from "./ZigZagMarra.astro";
interface Props {
isAbsolutePosition?: boolean;
}
const { isAbsolutePosition = false } = Astro.props;
const currentPath = Astro.url.pathname;
---
<header class={isAbsolutePosition ? "absolute-position" : ""}>
{
currentPath != "/" ? (
<a href="/">
<span class="visually-hidden">Laba</span>
<Logo class="logo" />
</a>
) : (
<address>
<p>Gazteluko plaza, 2</p>
<p>Iruñea</p>
</address>
)
}
<button class="mobile-only menu-button" id="ham-btn"><Hamburger /></button>
<nav class="desktop-only">
<h2>Menua</h2>
<ul>
<li><a href="/#agenda">Agenda</a></li>
<li><a href="/#izan-labazkide">Izan Labazkide</a></li>
<li><a href="/#parte-hartu">Parte hartu</a></li>
</ul>
</nav>
<nav class="desktop-only">
<h2>Jarrai gaitzazu!</h2>
<ul>
<li><a href="https://mastodon.eus/@labasarea">Mastodon</a></li>
<li><a href="https://www.instagram.com/labasarea/">Instagram</a></li>
<li><a href="https://twitter.com/labasarea/">X</a></li>
</ul>
</nav>
<address class="desktop-only">
<h2>Harremanetarako</h2>
<a href="mailto:info@laba.eus">info@laba.eus</a>
</address>
</header>
<dialog id="ham-dialog">
<button class="close-button" id="close-dialog"
><Close class="menu-button" /></button
>
<nav>
<h2>Menua</h2>
<ul>
<li><a href="/#agenda">Agenda</a></li>
<li><a href="/#izan-labazkide">Izan Labazkide</a></li>
<li><a href="/#parte-hartu">Parte hartu</a></li>
</ul>
</nav>
<nav>
<h2>Jarrai gaitzazu!</h2>
<ul>
<li><a href="https://mastodon.eus/@labasarea">Mastodon</a></li>
<li><a href="https://www.instagram.com/labasarea/">Instagram</a></li>
<li><a href="https://twitter.com/labasarea/">X</a></li>
</ul>
</nav>
<address>
<h2>Harremanetarako</h2>
<a href="mailto:info@laba.eus">info@laba.eus</a>
</address>
<ZigZagMarra />
{
currentPath === "/" ? (
<a class="dialog-footer" href="/">
<span class="visually-hidden">Laba</span>
<Logo class="logo" />
</a>
) : (
<address class="dialog-footer">
<p>Gazteluko plaza, 2</p>
<p>Iruñea</p>
</address>
)
}
</dialog>
<script is:inline>
const hamBtn = document.getElementById("ham-btn");
const dialog = document.getElementById("ham-dialog");
const closeBtn = document.getElementById("close-dialog");
hamBtn?.addEventListener("click", () => {
if (dialog) {
dialog.showModal();
// Esperar un tick para que el navegador "pinte" el open
requestAnimationFrame(() => {
dialog.classList.add("show");
});
document.body.classList.add("no-scroll");
}
});
closeBtn?.addEventListener("click", () => {
if (dialog) {
dialog.classList.remove("show");
dialog.classList.add("hide");
setTimeout(() => {
dialog.close();
dialog.classList.remove("hide");
}, 300);
document.body.classList.remove("no-scroll");
}
});
</script>
<style lang="scss">
@use "../../style/mixins/textFonts.scss";
@use "../../style/tools/mediaQueries.scss";
header {
display: flex;
justify-content: space-between;
align-items: flex-start;
padding: var(--space-05-mobile);
z-index: 15;
color: var(--gorria);
@include mediaQueries.aboveTablet {
display: grid;
grid-template-columns: 1fr 1.5fr 1fr 1fr;
padding: var(--space-07-desktop);
}
}
.absolute-position {
position: absolute;
top: 0;
left: 0;
right: 0;
}
.desktop-only {
@include mediaQueries.belowTablet {
display: none;
}
}
.mobile-only {
@include mediaQueries.aboveTablet {
display: none;
}
}
.logo {
width: var(--space-08-mobile);
height: auto;
@include mediaQueries.aboveTablet {
width: var(--space-09-desktop);
}
}
button {
padding: 0;
color: var(--gorria);
border: none;
background-color: transparent;
}
.menu-button {
width: var(--space-06-mobile);
height: auto;
}
address {
font-style: normal;
}
h2 {
text-transform: uppercase;
@include textFonts.bodyFont;
}
ul {
display: flex;
gap: var(--space-03-mobile);
padding: 0;
list-style: none;
@include mediaQueries.aboveTablet {
gap: var(--space-03-desktop);
}
}
li {
&:not(:last-child) {
&::after {
content: ",";
}
}
}
a {
color: var(--gorria);
text-decoration: underline;
&:hover {
text-decoration: none;
}
}
.close-button {
position: absolute;
right: var(--space-05-mobile);
top: var(--space-05-mobile);
}
dialog {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 100dvw;
height: 100dvh;
padding: var(--space-08-mobile) var(--space-05-mobile)
var(--space-05-mobile) var(--space-05-mobile);
color: var(--gorria);
border: none;
opacity: 0;
transform: translate(-50%, -70%); /* un poco más arriba */
transition: all 0.3s ease-out;
&.show {
display: grid;
grid-template-rows: auto auto auto auto 1fr;
gap: var(--space-05-mobile);
opacity: 1;
transform: translate(-50%, -50%); /* centrado */
}
&.hide {
opacity: 0;
transform: translate(-50%, -70%); /* se va hacia arriba */
}
&::backdrop {
background: var(--gorria);
}
&.hide::backdrop {
background: transparent;
}
}
.dialog-footer {
justify-self: center;
align-self: flex-end;
}
</style>

View File

@@ -0,0 +1,149 @@
---
import { getImageData, type ImageMedia } from "models/ImageMedia";
interface Props {
mosaikoa: ImageMedia[];
}
const { mosaikoa } = Astro.props;
const mosaikoaImageDatas = shuffle(mosaikoa).map(getImageData);
const imagesToShow = Array.from({ length: 60 }).map(
(_, i) => mosaikoaImageDatas[i % mosaikoaImageDatas.length]
);
function shuffle<T>(array: T[]) {
const copia = [...array];
for (let i = copia.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[copia[i], copia[j]] = [copia[j], copia[i]];
}
return copia;
}
---
<div class="wrapper">
<div class="grid">
{
imagesToShow.map((imageData) => (
<div class="cell">
<img
src={imageData.src}
alt={imageData.alt}
width={imageData.width}
height={imageData.height}
/>
</div>
))
}
</div>
</div>
<script>
import gsap from "gsap";
const cells = document.querySelectorAll(".cell img");
cells.forEach((img) => {
img?.parentElement?.addEventListener("mouseenter", () => {
gsap.to(img, {
scale: 2,
opacity: 1,
duration: 0.3,
ease: "power2.out",
});
});
img?.parentElement?.addEventListener("mouseleave", () => {
gsap.to(img, {
scale: 0,
opacity: 0.4,
duration: 1.6,
ease: "power2.inOut",
});
});
img?.parentElement?.addEventListener("touchstart", () => {
gsap.to(img, {
scale: 2,
opacity: 1,
duration: 0.3,
ease: "power2.out",
});
});
img?.parentElement?.addEventListener("touchend", () => {
gsap.to(img, {
scale: 0,
opacity: 0.4,
duration: 1.6,
ease: "power2.inOut",
});
});
});
</script>
<style lang="scss">
@use "../../style/tools/mediaQueries.scss";
.wrapper {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 130%;
overflow: hidden;
background: transparent;
padding: var(--space-07-mobile);
z-index: 10;
}
.grid {
display: grid;
grid-template-columns: repeat(5, 1fr);
grid-template-rows: repeat(6, 1fr);
background: var(--marroia);
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 97dvh;
@include mediaQueries.aboveTablet {
grid-template-columns: repeat(10, 1fr);
grid-template-rows: repeat(6, 1fr);
}
}
.cell {
position: relative;
height: 100%;
width: 100%;
overflow: visible;
&:nth-child(n + 31) {
display: none;
}
@include mediaQueries.aboveTablet {
&:nth-child(n + 31) {
display: block;
}
}
}
.cell img {
position: absolute;
width: 100%;
height: auto;
transform: scale(0);
opacity: 0;
will-change: transform, opacity;
object-fit: contain;
z-index: 10;
}
</style>

View File

@@ -0,0 +1,135 @@
---
import { marked } from "marked";
import type { Markdown } from "models/Markdown";
interface Props {
content: Markdown;
}
const { content } = Astro.props;
---
<div class="markdown-wrapper" set:html={marked.parse(content, {})} />
<!-- global jartzen dugu, marked-en sortutako HTMLa estilizatzeko -->
<style lang="scss" is:global>
@use "../../style/mixins/displayFonts.scss";
@use "../../style/tools/mediaQueries.scss";
.markdown-wrapper {
p {
&:not(:last-child) {
margin-bottom: var(--space-04-mobile);
}
@include mediaQueries.aboveTablet {
&:not(:last-child) {
margin-bottom: var(--space-05-desktop);
}
}
}
h2 {
margin-bottom: var(--space-04-mobile);
@include displayFonts.display2;
@include mediaQueries.aboveTablet {
margin-bottom: var(--space-04-desktop);
}
}
h3 {
margin-bottom: var(--space-03-mobile);
@include displayFonts.display3;
@include mediaQueries.aboveTablet {
margin-bottom: var(--space-04-desktop);
}
}
h4 {
margin-bottom: var(--space-02-mobile);
@include displayFonts.display4;
@include mediaQueries.aboveTablet {
margin-bottom: var(--space-03-desktop);
}
}
h4 {
margin-bottom: var(--space-02-mobile);
@include displayFonts.display4;
@include mediaQueries.aboveTablet {
margin-bottom: var(--space-03-desktop);
}
}
h5 {
margin-bottom: var(--space-01-mobile);
@include displayFonts.display5;
@include mediaQueries.aboveTablet {
margin-bottom: var(--space-02-desktop);
}
}
h6 {
margin-bottom: var(--space-01-mobile);
@include displayFonts.display6;
@include mediaQueries.aboveTablet {
margin-bottom: var(--space-02-desktop);
}
}
a {
color: var(--marroia);
text-decoration: underline;
&:hover {
text-decoration: none;
}
}
li {
width: 100%;
margin-bottom: var(--space-01-mobile);
overflow-wrap: break-word;
@include mediaQueries.aboveTablet {
margin-bottom: var(--space-02-desktop);
}
}
ul ul {
margin-top: var(--space-01-mobile);
@include mediaQueries.aboveTablet {
margin-top: var(--space-02-desktop);
}
}
hr {
width: 100%;
height: 2px;
margin-top: var(--space-04-mobile);
margin-bottom: var(--space-04-mobile);
border: none;
background-color: var(--gorria);
@include mediaQueries.aboveTablet {
margin-top: var(--space-05-desktop);
margin-bottom: var(--space-05-desktop);
}
}
}
</style>

View File

@@ -0,0 +1,44 @@
---
type Kolorea = "transparent" | "gorria" | "gorria-background" | "marroia";
interface Props {
from?: Kolorea;
to?: Kolorea;
}
const { from = "transparent", to = "transparent" } = Astro.props;
const colorToVar: { [key in Kolorea]: string } = {
transparent: "transparent",
gorria: "var(--gorria)",
marroia: "var(--marroia)",
"gorria-background": "var(--gorria-background)",
};
const fromColor = colorToVar[from];
const toColor = colorToVar[to];
---
<div>
<hr />
</div>
<style lang="scss" define:vars={{ toColor, fromColor }}>
hr {
height: 16px;
background-color: var(--fromColor);
border: none;
mask: conic-gradient(
from -45deg at bottom,
#0000,
#000 1deg 89deg,
#0000 90deg
)
50%/32px 100%;
}
div {
background-color: var(--toColor);
}
</style>

View File

@@ -0,0 +1,41 @@
---
// TODO marrak elementuaren altu osoa hartzea lortu, marginak fidagarriagoak izan daitezen
---
<div>
<hr class="zig" />
<hr class="zag" />
</div>
<style>
hr {
border: none;
height: 32px;
margin: 0px;
}
.zig {
background:
linear-gradient(-135deg, #fff 20px, transparent 0) 0 5px,
linear-gradient(135deg, #fff 20px, transparent 0) 0 5px;
background-color: transparent;
background-position: center bottom;
background-repeat: repeat-x;
background-size: 20px 40px;
z-index: 100;
position: relative;
}
.zag {
/* oraingoz, kolore gorria bakarrik margotzen du */
/* TODO beharrezko bada, kolorea currentColor bidez aldatu */
background:
linear-gradient(-135deg, var(--gorria) 20px, transparent 0) 0 5px,
linear-gradient(135deg, var(--gorria) 20px, #fff 0) 0 5px;
background-color: transparent;
background-position: center bottom;
background-repeat: repeat-x;
background-size: 20px 40px;
margin-top: -31px;
}
</style>

View File

@@ -0,0 +1,94 @@
@use "../../../../style/mixins/displayFonts.scss";
@use "../../../../style/tools/mediaQueries.scss";
@use "sass:color";
.wrapper {
display: grid;
grid-template-columns: repeat(12, [col-start] 1fr);
gap: var(--space-05-desktop);
}
.mainMediaWrapper {
display: none;
@include mediaQueries.aboveTablet {
display: grid;
grid-column: col-start 1 / span 4;
aspect-ratio: 1;
}
}
.mainMedia {
border-radius: 50%;
aspect-ratio: 1;
width: 100%;
height: auto;
object-fit: cover;
}
.agendaWrapper {
grid-column: col-start 1 / -1;
color: var(--gorria);
@include mediaQueries.aboveTablet {
grid-column: col-start 5 / -1;
}
}
.description {
margin-top: var(--space-05-desktop);
}
.zerrenda {
display: flex;
flex-direction: column;
gap: var(--space-04-mobile);
margin-top: var(--space-05-mobile);
list-style-type: none;
padding: 0;
@include mediaQueries.aboveTablet {
gap: var(--space-04-desktop);
margin-top: var(--space-05-desktop);
}
}
.esteka {
display: grid;
gap: var(--space-04-mobile);
grid-template-columns: 1fr auto;
justify-content: space-between;
width: 100%;
position: relative;
// irudi estelaren wrapper-a ekiditeko z-index ezartzen da
z-index: 20;
& {
color: var(--gorria);
opacity: 0.65;
text-decoration: none;
}
&.active {
opacity: 1;
}
@include displayFonts.display5;
@include mediaQueries.aboveTablet {
gap: var(--space-04-desktop);
}
}
.izenburua {
position: relative;
// irudi estelaren wrapper-a ekiditeko z-index ezartzen da
z-index: 20;
@include displayFonts.display2;
}

View File

@@ -0,0 +1,124 @@
import React, { useState, useEffect, useRef, useMemo } from "react";
import gsap from "gsap";
import dayjs from "dayjs";
import { type EkintzaSnippet, getShortDate, getUrl } from "models/Ekintza";
import { getImageData, type ImageData } from "models/ImageMedia";
import styles from "./EkintzaZerrenda.module.scss";
interface Props {
ekintzak: EkintzaSnippet[];
title?: string;
description?: string;
isMainTitle?: boolean;
}
export const EkintzaZerrenda: React.FC<Props> = ({
title,
description,
ekintzak,
isMainTitle = false,
}) => {
const sortedEkintzak = useMemo(
() =>
[...ekintzak].sort((a, b) =>
dayjs(a.hitzordua).isBefore(b.hitzordua) ? -1 : 1
),
[ekintzak]
);
const [activeEkintza, setActiveEkintza] = useState<EkintzaSnippet>(
sortedEkintzak[0]
);
const [playInterval, setPlayInterval] = useState(true);
const imageToShow = activeEkintza?.mainMedia
? getImageData(activeEkintza.mainMedia)
: undefined;
const imgRef = useRef<HTMLImageElement | null>(null);
const intervalRef = useRef<NodeJS.Timeout | null>(null);
useEffect(() => {
intervalRef.current = setInterval(() => {
if (!playInterval) {
return;
}
setActiveEkintza(
sortedEkintzak[Math.floor(Math.random() * sortedEkintzak.length)]
);
}, 1800);
return () => {
if (intervalRef.current) clearInterval(intervalRef.current);
};
}, [playInterval]);
useEffect(() => {
if (imgRef.current) {
gsap.fromTo(
imgRef.current,
{ autoAlpha: 0, scale: 1.05 },
{ autoAlpha: 1, scale: 1, duration: 0.5, ease: "power2.out" }
);
}
}, [activeEkintza]);
return (
<div className={styles.wrapper}>
<div aria-hidden className={styles.mainMediaWrapper}>
{imageToShow && (
<img
ref={imgRef}
className={styles.mainMedia}
alt={imageToShow.alt}
src={imageToShow.src}
width={imageToShow.width}
height={imageToShow.height}
/>
)}
</div>
<div className={styles.agendaWrapper}>
{title && (
<>
{isMainTitle ? (
<h1 className={styles.izenburua}>{title}</h1>
) : (
<h2 className={styles.izenburua}>{title}</h2>
)}
</>
)}
{description && <p className={styles.description}>{description}</p>}
<ul className={styles.zerrenda}>
{sortedEkintzak.map((ekintza) => (
<li
key={ekintza.id}
onMouseEnter={() => handleMouseEnter(ekintza)}
onMouseLeave={handleMouseLeave}
>
<a
className={`${styles.esteka} ${activeEkintza?.id === ekintza.id && styles.active}`}
href={getUrl(ekintza)}
>
<span>{ekintza.izenburua}</span>
<span>{getShortDate(ekintza)}</span>
</a>
</li>
))}
</ul>
</div>
</div>
);
function handleMouseEnter(ekintza: EkintzaSnippet) {
setPlayInterval(false);
setActiveEkintza(ekintza);
}
function handleMouseLeave() {
setPlayInterval(true);
}
};

View File

@@ -0,0 +1 @@
export { EkintzaZerrenda } from "./EkintzaZerrenda";