Skip to content

Commit 585050b

Browse files
committed
Cambios sección proyectos
1 parent 135c31a commit 585050b

3 files changed

Lines changed: 182 additions & 12 deletions

File tree

assets/css/styles.css

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -530,3 +530,78 @@ p{ color: var(--muted); line-height: 1.7; }
530530
grid-template-columns: 1fr;
531531
}
532532
}
533+
534+
/* =========================
535+
PROYECTOS — Carrusel (Opción A)
536+
========================= */
537+
.carousel{
538+
display: grid;
539+
grid-template-columns: 48px 1fr 48px;
540+
gap: 14px;
541+
align-items: center;
542+
}
543+
544+
.carousel-stage{
545+
min-height: 320px; /* evita saltos al cambiar */
546+
}
547+
548+
.carousel-btn{
549+
height: 52px;
550+
border-radius: 14px;
551+
border: 1px solid rgba(255,255,255,.18);
552+
background: rgba(0,0,0,.18);
553+
color: var(--text);
554+
font-size: 28px;
555+
font-weight: 800;
556+
cursor: pointer;
557+
transition: transform .15s ease, border-color .15s ease, background .15s ease;
558+
}
559+
560+
.carousel-btn:hover{
561+
transform: translateY(-2px);
562+
border-color: rgba(255,255,255,.32);
563+
background: rgba(0,0,0,.26);
564+
}
565+
566+
.carousel-btn:active{
567+
transform: translateY(0);
568+
}
569+
570+
.carousel-dots{
571+
display: flex;
572+
justify-content: center;
573+
gap: 10px;
574+
margin-top: 14px;
575+
}
576+
577+
.carousel-dot{
578+
width: 10px;
579+
height: 10px;
580+
border-radius: 999px;
581+
border: 1px solid rgba(255,255,255,.20);
582+
background: rgba(255,255,255,.10);
583+
cursor: pointer;
584+
opacity: .85;
585+
transition: transform .15s ease, opacity .15s ease;
586+
}
587+
588+
.carousel-dot:hover{
589+
transform: scale(1.15);
590+
opacity: 1;
591+
}
592+
593+
.carousel-dot.active{
594+
background: rgba(255,255,255,.55);
595+
opacity: 1;
596+
}
597+
598+
/* En móvil: botones un poco más pequeños */
599+
@media (max-width: 768px){
600+
.carousel{
601+
grid-template-columns: 44px 1fr 44px;
602+
gap: 10px;
603+
}
604+
.carousel-stage{
605+
min-height: 360px;
606+
}
607+
}

assets/js/app.js

Lines changed: 96 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -164,13 +164,13 @@ function setupContactForm(){
164164
/* =========================================================
165165
RENDER: PROJECTS
166166
========================================================= */
167-
function renderProjects(){
168-
const grid = document.getElementById("projects-grid");
169-
if (!grid || !STATE.dict?.projects) return;
167+
let PROJECT_INDEX = 0;
170168

171-
const proj = STATE.dict.projects;
169+
function getProjectItems(){
170+
const proj = STATE.dict?.projects;
171+
if (!proj) return [];
172172

173-
const items = [
173+
return [
174174
{
175175
title: proj.p1_title,
176176
desc: proj.p1_desc,
@@ -196,22 +196,107 @@ function renderProjects(){
196196
emoji: "🔍"
197197
}
198198
];
199+
}
199200

200-
grid.innerHTML = items.map(p => `
201+
function clampIndex(i, len){
202+
if (len <= 0) return 0;
203+
return (i + len) % len; // wrap circular
204+
}
205+
206+
function renderProjects(){
207+
const stage = document.getElementById("project-stage");
208+
const dots = document.getElementById("project-dots");
209+
if (!stage || !dots) return;
210+
211+
const items = getProjectItems();
212+
if (items.length === 0) {
213+
stage.innerHTML = "";
214+
dots.innerHTML = "";
215+
return;
216+
}
217+
218+
PROJECT_INDEX = clampIndex(PROJECT_INDEX, items.length);
219+
const p = items[PROJECT_INDEX];
220+
221+
stage.innerHTML = `
201222
<div class="card project-card">
202-
<div class="project-thumb"><span>${p.emoji}</span></div>
223+
<div class="project-thumb">
224+
<span>${p.emoji}</span>
225+
</div>
226+
203227
<h3>${p.title}</h3>
204228
<p>${p.desc}</p>
205-
<div class="tech">${p.tech.map(t => `<span>${t}</span>`).join("")}</div>
229+
230+
<div class="tech">
231+
${p.tech.map(t => `<span>${t}</span>`).join("")}
232+
</div>
233+
206234
<details>
207-
<summary>${STATE.lang === "es" ? "+ info" : "+ info"}</summary>
235+
<summary>+ info</summary>
208236
<p>${p.why}</p>
209237
</details>
210-
<a href="${p.link}" class="btn btn--primary" target="_blank" rel="noopener noreferrer">GitHub</a>
238+
239+
<a href="${p.link}" class="btn" target="_blank" rel="noopener noreferrer">GitHub</a>
211240
</div>
241+
`;
242+
243+
dots.innerHTML = items.map((_, idx) => `
244+
<button
245+
class="carousel-dot ${idx === PROJECT_INDEX ? "active" : ""}"
246+
aria-label="Ir al proyecto ${idx + 1}"
247+
data-idx="${idx}">
248+
</button>
212249
`).join("");
250+
251+
// dots click
252+
dots.querySelectorAll(".carousel-dot").forEach(b=>{
253+
b.addEventListener("click", ()=>{
254+
PROJECT_INDEX = Number(b.getAttribute("data-idx"));
255+
renderProjects();
256+
});
257+
});
213258
}
214259

260+
// Botones prev/next + swipe
261+
function setupProjectsCarousel(){
262+
const prev = document.getElementById("proj-prev");
263+
const next = document.getElementById("proj-next");
264+
const stage = document.getElementById("project-stage");
265+
if (!prev || !next || !stage) return;
266+
267+
prev.addEventListener("click", ()=>{
268+
const items = getProjectItems();
269+
PROJECT_INDEX = clampIndex(PROJECT_INDEX - 1, items.length);
270+
renderProjects();
271+
});
272+
273+
next.addEventListener("click", ()=>{
274+
const items = getProjectItems();
275+
PROJECT_INDEX = clampIndex(PROJECT_INDEX + 1, items.length);
276+
renderProjects();
277+
});
278+
279+
// Swipe simple (móvil)
280+
let x0 = null;
281+
stage.addEventListener("touchstart", (e)=>{
282+
x0 = e.touches?.[0]?.clientX ?? null;
283+
}, { passive:true });
284+
285+
stage.addEventListener("touchend", (e)=>{
286+
const x1 = e.changedTouches?.[0]?.clientX ?? null;
287+
if (x0 == null || x1 == null) return;
288+
289+
const dx = x1 - x0;
290+
if (Math.abs(dx) < 60) return;
291+
292+
const items = getProjectItems();
293+
PROJECT_INDEX = clampIndex(PROJECT_INDEX + (dx < 0 ? 1 : -1), items.length);
294+
renderProjects();
295+
x0 = null;
296+
}, { passive:true });
297+
}
298+
299+
215300
/* =========================================================
216301
RENDER: SKILLS
217302
========================================================= */
@@ -285,6 +370,7 @@ document.addEventListener("DOMContentLoaded", () => {
285370
setupSearch();
286371
setupContactForm();
287372
setupSectionObserver();
373+
setupProjectsCarousel();
288374

289375
loadDict(STATE.lang);
290376
});

index.html

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,10 +97,19 @@ <h2 data-i18n="skills.title">Skills</h2>
9797

9898
<section id="projects" class="section">
9999
<h2 data-i18n="projects.title">Proyectos</h2>
100-
<div id="projects-grid"></div>
101-
<!-- Nota: esta sección la reestructuramos después de elegir un estilo (carrusel / modal / scroll-snap). -->
100+
101+
<div class="carousel" aria-label="Carrusel de proyectos">
102+
<button class="carousel-btn" id="proj-prev" aria-label="Proyecto anterior"></button>
103+
104+
<div id="project-stage" class="carousel-stage"></div>
105+
106+
<button class="carousel-btn" id="proj-next" aria-label="Proyecto siguiente"></button>
107+
</div>
108+
109+
<div id="project-dots" class="carousel-dots" aria-label="Selector de proyecto"></div>
102110
</section>
103111

112+
104113
<section id="xp" class="section">
105114
<h2 data-i18n="xp.title">Experiencia & Certificaciones</h2>
106115
<div id="xp-list"></div>

0 commit comments

Comments
 (0)