rss bug fixes
This commit is contained in:
@@ -13,7 +13,6 @@ pub struct Project {
|
|||||||
pub code_url: String,
|
pub code_url: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Deine vordefinierte Liste
|
|
||||||
fn get_all_projects() -> Vec<Project> {
|
fn get_all_projects() -> Vec<Project> {
|
||||||
vec![
|
vec![
|
||||||
Project {
|
Project {
|
||||||
@@ -27,12 +26,20 @@ fn get_all_projects() -> Vec<Project> {
|
|||||||
Project {
|
Project {
|
||||||
id: 2,
|
id: 2,
|
||||||
title: "Anderes Projekt".into(),
|
title: "Anderes Projekt".into(),
|
||||||
description: "Beschreibung deines zweiten Projekts hier.".into(),
|
description: "Ein weiteres Beispielprojekt mit einer etwas längeren Beschreibung, um das Grid zu testen.".into(),
|
||||||
image_url: "/assets/bytemalte_de.png".into(),
|
image_url: "/assets/bytemalte_de.png".into(),
|
||||||
download_url: "#".into(),
|
download_url: "#".into(),
|
||||||
code_url: "#".into(),
|
code_url: "#".into(),
|
||||||
},
|
},
|
||||||
// Füge hier weitere hinzu...
|
Project {
|
||||||
|
id: 3,
|
||||||
|
title: "Projekt Drei".into(),
|
||||||
|
description: "Hier ist ein drittes Projekt, das ebenfalls in der Liste erscheint.".into(),
|
||||||
|
image_url: "/assets/Logo.jpg".into(),
|
||||||
|
download_url: "#".into(),
|
||||||
|
code_url: "#".into(),
|
||||||
|
},
|
||||||
|
// Füge hier weitere Projekte hinzu...
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -41,14 +48,13 @@ pub fn HomePage() -> impl IntoView {
|
|||||||
let items_per_page = 3;
|
let items_per_page = 3;
|
||||||
let all_data = get_all_projects();
|
let all_data = get_all_projects();
|
||||||
|
|
||||||
// WICHTIG: Wir laden die erste Seite direkt hier für das SSR!
|
// Initialer Zustand für SSR & Client
|
||||||
let initial_projects = all_data.iter().take(items_per_page).cloned().collect::<Vec<_>>();
|
let initial_projects = all_data.iter().take(items_per_page).cloned().collect::<Vec<_>>();
|
||||||
let has_more_initially = all_data.len() > items_per_page;
|
|
||||||
|
|
||||||
let (projects, set_projects) = signal(initial_projects);
|
let (projects, set_projects) = signal(initial_projects);
|
||||||
let (page, set_page) = signal(1);
|
let (page, set_page) = signal(1);
|
||||||
let (loading, set_loading) = signal(false);
|
let (loading, set_loading) = signal(false);
|
||||||
let (all_loaded, set_all_loaded) = signal(!has_more_initially);
|
let (all_loaded, set_all_loaded) = signal(all_data.len() <= items_per_page);
|
||||||
|
|
||||||
let sentinel = NodeRef::<Div>::new();
|
let sentinel = NodeRef::<Div>::new();
|
||||||
|
|
||||||
@@ -66,7 +72,7 @@ pub fn HomePage() -> impl IntoView {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Simuliere Netzwerk-Delay für den Spinner
|
// Kurze Verzögerung für den Lade-Effekt
|
||||||
set_timeout(move || {
|
set_timeout(move || {
|
||||||
let slice = all_my_projects[start..end].to_vec();
|
let slice = all_my_projects[start..end].to_vec();
|
||||||
set_projects.update(|projs| projs.extend(slice));
|
set_projects.update(|projs| projs.extend(slice));
|
||||||
@@ -75,10 +81,9 @@ pub fn HomePage() -> impl IntoView {
|
|||||||
set_all_loaded.set(true);
|
set_all_loaded.set(true);
|
||||||
}
|
}
|
||||||
set_loading.set(false);
|
set_loading.set(false);
|
||||||
}, std::time::Duration::from_millis(300));
|
}, std::time::Duration::from_millis(200));
|
||||||
};
|
};
|
||||||
|
|
||||||
// Scroll-Event-Listener (Nur im Browser aktiv)
|
|
||||||
Effect::new(move |_| {
|
Effect::new(move |_| {
|
||||||
let w = window();
|
let w = window();
|
||||||
let w_clone = w.clone();
|
let w_clone = w.clone();
|
||||||
@@ -92,10 +97,10 @@ pub fn HomePage() -> impl IntoView {
|
|||||||
let inner_height = w_clone.inner_height().unwrap().as_f64().unwrap_or(0.0);
|
let inner_height = w_clone.inner_height().unwrap().as_f64().unwrap_or(0.0);
|
||||||
let total_height = el.offset_height() as f64;
|
let total_height = el.offset_height() as f64;
|
||||||
|
|
||||||
if scroll_y + inner_height >= total_height - 250.0 {
|
if scroll_y + inner_height >= total_height - 300.0 {
|
||||||
let next_page = page.get_untracked();
|
let current_page = page.get_untracked();
|
||||||
set_page.set(next_page + 1);
|
set_page.set(current_page + 1);
|
||||||
fetch_projects(next_page);
|
fetch_projects(current_page);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -110,7 +115,8 @@ pub fn HomePage() -> impl IntoView {
|
|||||||
<p>"Entdecke meine neuesten Projekte."</p>
|
<p>"Entdecke meine neuesten Projekte."</p>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<main class="projects-grid">
|
// Der Container MUSS die Klasse projects-grid haben
|
||||||
|
<div class="projects-grid">
|
||||||
<For
|
<For
|
||||||
each=move || projects.get()
|
each=move || projects.get()
|
||||||
key=|p| p.id
|
key=|p| p.id
|
||||||
@@ -124,15 +130,15 @@ pub fn HomePage() -> impl IntoView {
|
|||||||
<h3>{p.title}</h3>
|
<h3>{p.title}</h3>
|
||||||
<p>{p.description}</p>
|
<p>{p.description}</p>
|
||||||
<div class="project-links">
|
<div class="project-links">
|
||||||
<a href=p.download_url target="_blank" class="btn-download">"Download"</a>
|
<a href=p.download_url target="_blank" rel="noopener noreferrer" class="btn-download">"Download"</a>
|
||||||
<a href=p.code_url target="_blank" class="btn-code">"Source Code"</a>
|
<a href=p.code_url target="_blank" rel="noopener noreferrer" class="btn-code">"Source Code"</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</main>
|
</div>
|
||||||
|
|
||||||
<div node_ref=sentinel class="loading-trigger">
|
<div node_ref=sentinel class="loading-trigger">
|
||||||
{move || match (loading.get(), all_loaded.get()) {
|
{move || match (loading.get(), all_loaded.get()) {
|
||||||
|
|||||||
@@ -1,66 +1,63 @@
|
|||||||
.home-container {
|
.home-container {
|
||||||
max-width: 1200px;
|
max-width: 1100px;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
padding: 0 1.5rem;
|
padding: 0 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.projects-grid {
|
.projects-grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
|
// Sorgt dafür, dass die Karten immer gleich breit sind
|
||||||
|
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
|
||||||
gap: 2rem;
|
gap: 2rem;
|
||||||
margin-top: 2rem;
|
margin: 2rem 0;
|
||||||
|
// Verhindert Layout-Shifts
|
||||||
|
align-items: stretch;
|
||||||
}
|
}
|
||||||
|
|
||||||
.project-card {
|
.project-card {
|
||||||
background: var(--white);
|
background: var(--white);
|
||||||
border-radius: 1.25rem;
|
border-radius: 1rem;
|
||||||
overflow: hidden;
|
|
||||||
border: 1px solid var(--border);
|
border: 1px solid var(--border);
|
||||||
transition: transform 0.3s ease, box-shadow 0.3s ease;
|
overflow: hidden;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
||||||
|
height: 100%; // Wichtig für einheitliche Höhe im Grid
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
transform: translateY(-8px);
|
transform: translateY(-5px);
|
||||||
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1);
|
box-shadow: 0 10px 20px rgba(0,0,0,0.05);
|
||||||
}
|
}
|
||||||
|
|
||||||
.project-image {
|
.project-image {
|
||||||
width: 100%;
|
height: 180px;
|
||||||
height: 200px;
|
background: #f1f5f9;
|
||||||
background: #eef2ff;
|
|
||||||
overflow: hidden;
|
|
||||||
|
|
||||||
img {
|
img {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
transition: transform 0.5s ease;
|
display: block;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover .project-image img {
|
|
||||||
transform: scale(1.05);
|
|
||||||
}
|
|
||||||
|
|
||||||
.project-content {
|
.project-content {
|
||||||
padding: 1.5rem;
|
padding: 1.25rem;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
flex: 1;
|
flex-grow: 1;
|
||||||
|
|
||||||
h3 {
|
h3 {
|
||||||
margin: 0 0 0.75rem 0;
|
margin: 0 0 0.5rem 0;
|
||||||
font-size: 1.25rem;
|
font-size: 1.25rem;
|
||||||
color: var(--text-main);
|
color: var(--text-main);
|
||||||
}
|
}
|
||||||
|
|
||||||
p {
|
p {
|
||||||
margin: 0 0 1.5rem 0;
|
|
||||||
color: var(--text-light);
|
|
||||||
font-size: 0.95rem;
|
font-size: 0.95rem;
|
||||||
line-height: 1.6;
|
color: var(--text-light);
|
||||||
flex: 1;
|
line-height: 1.5;
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
flex-grow: 1; // Drückt die Buttons nach unten
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,13 +67,14 @@
|
|||||||
gap: 0.75rem;
|
gap: 0.75rem;
|
||||||
|
|
||||||
a {
|
a {
|
||||||
|
display: inline-block;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
padding: 0.6rem;
|
padding: 0.6rem;
|
||||||
border-radius: 0.75rem;
|
border-radius: 0.5rem;
|
||||||
font-size: 0.85rem;
|
font-size: 0.85rem;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
transition: all 0.2s;
|
transition: background 0.2s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-download {
|
.btn-download {
|
||||||
@@ -86,42 +84,35 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.btn-code {
|
.btn-code {
|
||||||
background: #f1f5f9;
|
background: var(--bg);
|
||||||
color: var(--text-main);
|
color: var(--text-main);
|
||||||
&:hover { background: #e2e8f0; }
|
border: 1px solid var(--border);
|
||||||
|
&:hover { background: var(--border); }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.loading-trigger {
|
.loading-trigger {
|
||||||
display: flex;
|
text-align: center;
|
||||||
justify-content: center;
|
padding: 3rem 0;
|
||||||
align-items: center;
|
min-height: 100px;
|
||||||
min-height: 150px;
|
}
|
||||||
padding-bottom: 50px;
|
|
||||||
color: var(--text-light);
|
|
||||||
|
|
||||||
p { font-size: 0.9rem; opacity: 0.7; }
|
.end-msg {
|
||||||
|
color: var(--text-light);
|
||||||
|
font-size: 0.9rem;
|
||||||
|
border-top: 1px solid var(--border);
|
||||||
|
padding-top: 2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.spinner {
|
.spinner {
|
||||||
width: 40px;
|
width: 30px;
|
||||||
height: 40px;
|
height: 30px;
|
||||||
border: 3px solid rgba(99, 102, 241, 0.1);
|
border: 3px solid var(--border);
|
||||||
border-top-color: var(--primary);
|
border-top-color: var(--primary);
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
|
margin: 0 auto;
|
||||||
animation: spin 0.8s linear infinite;
|
animation: spin 0.8s linear infinite;
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes spin {
|
@keyframes spin { to { transform: rotate(360deg); } }
|
||||||
to { transform: rotate(360deg); }
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 640px) {
|
|
||||||
.projects-grid {
|
|
||||||
grid-template-columns: 1fr;
|
|
||||||
}
|
|
||||||
.home-hero h1 {
|
|
||||||
font-size: 2.5rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user