Skip to content

Commit 6b594c2

Browse files
committed
fix: skeleton loaders now match actual content structure
- Summary strip: 4th skeleton uses summary-freshness (not summary-value) - Table skeleton: add ci-col, center, actions classes to td - Card skeleton: mirror 3-section layout (head, counts, ci) - Repo detail: skeleton rows during initial load instead of blank - Token form + load-more buttons: spinner animation while loading - Skeleton rows: pointer-events none, proper flex layout
1 parent 6825550 commit 6b594c2

4 files changed

Lines changed: 137 additions & 92 deletions

File tree

crates/app/src/components/token_form.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,10 +55,15 @@ pub fn TokenForm() -> impl IntoView {
5555
/>
5656
<button
5757
class="button-primary"
58+
class:button-loading=move || pending.get()
5859
on:click=move |_| { submit.dispatch(token.get()); }
5960
disabled=move || pending.get()
6061
>
61-
{move || if pending.get() { "Validating…" } else { "Save token" }}
62+
{move || if pending.get() {
63+
view! { <span class="button-spinner"></span> "Validating…" }.into_any()
64+
} else {
65+
"Save token".into_any()
66+
}}
6267
</button>
6368
{move || {
6469
error

crates/app/src/pages/dashboard.rs

Lines changed: 25 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -282,10 +282,10 @@ pub fn DashboardPage() -> impl IntoView {
282282
<span class="summary-value skeleton skeleton-line skeleton-line--sm"></span>
283283
<span class="summary-label">"Open PRs"</span>
284284
</div>
285-
<div class="summary-item">
286-
<span class="summary-value skeleton skeleton-line skeleton-line--sm"></span>
287-
<span class="summary-label">"Freshness"</span>
288-
</div>
285+
<div class="summary-item">
286+
<span class="summary-freshness skeleton skeleton-line skeleton-line--sm"></span>
287+
<span class="summary-label">"Freshness"</span>
288+
</div>
289289
</div>
290290
{if is_table {
291291
view! {
@@ -303,17 +303,17 @@ pub fn DashboardPage() -> impl IntoView {
303303
</tr>
304304
</thead>
305305
<tbody>
306-
{(0..8).map(|_| view! {
307-
<tr>
308-
<td><span class="skeleton skeleton-line"></span></td>
309-
<td class="num"><span class="skeleton skeleton-line skeleton-line--sm"></span></td>
310-
<td class="num"><span class="skeleton skeleton-line skeleton-line--sm"></span></td>
311-
<td><span class="skeleton skeleton-line skeleton-line--sm"></span></td>
312-
<td class="num"><span class="skeleton skeleton-line skeleton-line--sm"></span></td>
313-
<td><span class="skeleton skeleton-line skeleton-line--sm"></span></td>
314-
<td class="num"></td>
315-
</tr>
316-
}).collect_view()}
306+
{(0..8).map(|_| view! {
307+
<tr>
308+
<td><span class="skeleton skeleton-line"></span></td>
309+
<td class="num"><span class="skeleton skeleton-line skeleton-line--sm"></span></td>
310+
<td class="num"><span class="skeleton skeleton-line skeleton-line--sm"></span></td>
311+
<td class="ci-col"><span class="skeleton skeleton-line skeleton-line--sm"></span></td>
312+
<td class="num"><span class="skeleton skeleton-line skeleton-line--sm"></span></td>
313+
<td class="center"><span class="skeleton skeleton-line skeleton-line--sm"></span></td>
314+
<td class="actions"></td>
315+
</tr>
316+
}).collect_view()}
317317
</tbody>
318318
</table>
319319
</div>
@@ -324,8 +324,16 @@ pub fn DashboardPage() -> impl IntoView {
324324
<div class="repo-grid">
325325
{(0..6).map(|_| view! {
326326
<div class="skeleton-card">
327-
<div class="skeleton skeleton-line skeleton-line--lg"></div>
328-
<div class="skeleton skeleton-line skeleton-line--sm"></div>
327+
<div class="repo-card-head">
328+
<span class="skeleton skeleton-line skeleton-line--lg"></span>
329+
</div>
330+
<div class="repo-card-counts">
331+
<span class="skeleton skeleton-line skeleton-line--sm"></span>
332+
<span class="skeleton skeleton-line skeleton-line--sm"></span>
333+
</div>
334+
<div class="repo-card-ci">
335+
<span class="skeleton skeleton-line skeleton-line--sm"></span>
336+
</div>
329337
</div>
330338
}).collect_view()}
331339
</div>

crates/app/src/pages/repo_detail.rs

Lines changed: 100 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -326,48 +326,61 @@ pub fn RepoDetailPage() -> impl IntoView {
326326
let empty = items.is_empty();
327327
let loading = issues_loading.get();
328328
let has_more = issues_next.get().is_some();
329-
view! {
330-
<Show
331-
when=move || empty && !loading
332-
fallback=move || view! {
333-
<div class="row-list">
334-
{items
335-
.iter()
336-
.cloned()
337-
.map(|i| view! { <IssueRow issue=i/> })
338-
.collect_view()}
339-
{if has_more {
340-
view! {
341-
<button
342-
class="button-secondary load-more"
343-
disabled=move || loading
344-
on:click=move |_| {
345-
issues_loading.set(true);
346-
let fut = fetch_issues(issues_next.get());
347-
spawn_local(fut);
329+
if loading && empty {
330+
view! {
331+
<div class="row-list">
332+
{(0..5).map(|_| view! {
333+
<div class="row row--issue row-skeleton">
334+
<span class="row-num"><span class="skeleton skeleton-line" style="width:36px"></span></span>
335+
<span class="row-title"><span class="skeleton skeleton-line"></span></span>
336+
<span class="row-labels"><span class="skeleton skeleton-line" style="width:60px"></span></span>
337+
<span class="row-author"><span class="skeleton skeleton-line" style="width:64px"></span></span>
338+
<span class="row-comments"><span class="skeleton skeleton-line" style="width:50px"></span></span>
339+
</div>
340+
}).collect_view()}
341+
</div>
342+
}.into_any()
343+
} else if !empty {
344+
view! {
345+
<div class="row-list">
346+
{items
347+
.iter()
348+
.cloned()
349+
.map(|i| view! { <IssueRow issue=i/> })
350+
.collect_view()}
351+
{if has_more {
352+
view! {
353+
<button
354+
class="button-secondary load-more"
355+
disabled=move || loading
356+
on:click=move |_| {
357+
issues_loading.set(true);
358+
let fut = fetch_issues(issues_next.get());
359+
spawn_local(fut);
360+
}
361+
>
362+
{move || {
363+
if loading {
364+
view! { <span class="button-spinner"></span> "Loading…" }.into_any()
365+
} else {
366+
"Load more".into_any()
348367
}
349-
>
350-
{move || {
351-
if loading {
352-
"Loading…"
353-
} else {
354-
"Load more"
355-
}
356-
}}
357-
</button>
358-
}
359-
.into_any()
360-
} else {
361-
().into_any()
362-
}}
363-
</div>
364-
}
365-
>
368+
}}
369+
</button>
370+
}
371+
.into_any()
372+
} else {
373+
().into_any()
374+
}}
375+
</div>
376+
}.into_any()
377+
} else {
378+
view! {
366379
<div class="empty-state">
367380
<p class="body-strong">"No issues"</p>
368381
<p class="body-sm">"This repository has no issues matching the current filter."</p>
369382
</div>
370-
</Show>
383+
}.into_any()
371384
}
372385
}}
373386
</Show>
@@ -378,48 +391,61 @@ pub fn RepoDetailPage() -> impl IntoView {
378391
let empty = items.is_empty();
379392
let loading = pulls_loading.get();
380393
let has_more = pulls_next.get().is_some();
381-
view! {
382-
<Show
383-
when=move || empty && !loading
384-
fallback=move || view! {
385-
<div class="row-list">
386-
{items
387-
.iter()
388-
.cloned()
389-
.map(|p| view! { <PrRow pr=p/> })
390-
.collect_view()}
391-
{if has_more {
392-
view! {
393-
<button
394-
class="button-secondary load-more"
395-
disabled=move || loading
396-
on:click=move |_| {
397-
pulls_loading.set(true);
398-
let fut = fetch_pulls(pulls_next.get());
399-
spawn_local(fut);
394+
if loading && empty {
395+
view! {
396+
<div class="row-list">
397+
{(0..5).map(|_| view! {
398+
<div class="row row--pr row-skeleton">
399+
<span class="row-num"><span class="skeleton skeleton-line" style="width:36px"></span></span>
400+
<span class="row-title"><span class="skeleton skeleton-line"></span></span>
401+
<span class="row-labels"><span class="skeleton skeleton-line" style="width:60px"></span></span>
402+
<span class="row-author"><span class="skeleton skeleton-line" style="width:64px"></span></span>
403+
<span class="row-comments"><span class="skeleton skeleton-line" style="width:50px"></span></span>
404+
</div>
405+
}).collect_view()}
406+
</div>
407+
}.into_any()
408+
} else if !empty {
409+
view! {
410+
<div class="row-list">
411+
{items
412+
.iter()
413+
.cloned()
414+
.map(|p| view! { <PrRow pr=p/> })
415+
.collect_view()}
416+
{if has_more {
417+
view! {
418+
<button
419+
class="button-secondary load-more"
420+
disabled=move || loading
421+
on:click=move |_| {
422+
pulls_loading.set(true);
423+
let fut = fetch_pulls(pulls_next.get());
424+
spawn_local(fut);
425+
}
426+
>
427+
{move || {
428+
if loading {
429+
view! { <span class="button-spinner"></span> "Loading…" }.into_any()
430+
} else {
431+
"Load more".into_any()
400432
}
401-
>
402-
{move || {
403-
if loading {
404-
"Loading…"
405-
} else {
406-
"Load more"
407-
}
408-
}}
409-
</button>
410-
}
411-
.into_any()
412-
} else {
413-
().into_any()
414-
}}
415-
</div>
416-
}
417-
>
433+
}}
434+
</button>
435+
}
436+
.into_any()
437+
} else {
438+
().into_any()
439+
}}
440+
</div>
441+
}.into_any()
442+
} else {
443+
view! {
418444
<div class="empty-state">
419445
<p class="body-strong">"No pull requests"</p>
420446
<p class="body-sm">"This repository has no open pull requests."</p>
421447
</div>
422-
</Show>
448+
}.into_any()
423449
}
424450
}}
425451
</Show>

crates/app/src/style.css

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -589,6 +589,12 @@ a:hover {
589589
color: var(--colors-ash);
590590
font: var(--typography-caption-md);
591591
}
592+
.row-skeleton {
593+
pointer-events: none;
594+
}
595+
.row-skeleton .row-title {
596+
flex: 1;
597+
}
592598

593599
/* ---------- Filter & tab chips ---------- */
594600
.filter-chip {

0 commit comments

Comments
 (0)