Browse Source

fix blog-view

restore toc/article expansion
make collapsed cards less prominent
make entire expanded card clickable
responsive layout, for small screens
master
silberengel 3 months ago
parent
commit
71cb877847
  1. 143
      src/lib/components/cards/BlogHeader.svelte
  2. 79
      src/lib/components/publications/Publication.svelte
  3. 18
      src/lib/components/util/ArticleNav.svelte

143
src/lib/components/cards/BlogHeader.svelte

@ -87,60 +87,103 @@
</script> </script>
{#if title != null} {#if title != null}
<Card {#if active}
class="ArticleBox card-leather w-full grid max-w-xl {active <!-- Full card view when active -->
? 'active' <div
: ''}" class="ArticleBox card-leather w-full grid active cursor-pointer min-w-0"
> role="button"
<div class="space-y-4 relative"> tabindex={0}
<div class="flex flex-row justify-between my-2"> onclick={(e: MouseEvent) => {
<div class="flex flex-col"> // Don't trigger if clicking on CardActions or its children
{@render userBadge(authorPubkey, author, ndk)} const target = e.target as HTMLElement;
<span class="text-gray-700 dark:text-gray-300">{publishedAt()}</span> if (target.closest('.card-actions') || target.closest('button[type="button"]')) {
return;
}
showBlog();
}}
onkeydown={(e: KeyboardEvent) => {
if (e.key === 'Enter' || e.key === ' ') {
const target = e.target as HTMLElement;
if (!target.closest('.card-actions') && !target.closest('button[type="button"]')) {
showBlog();
}
}
}}
>
<Card class="w-full h-full min-w-0 !max-w-none">
<div class="space-y-4 relative pl-4 min-w-0 w-full">
<div class="flex flex-row justify-between my-2">
<div class="flex flex-col">
{@render userBadge(authorPubkey, author, ndk)}
<span class="text-gray-700 dark:text-gray-300">{publishedAt()}</span>
</div>
</div> </div>
</div>
<div <div
class="ArticleBoxImage flex justify-center items-center p-2 h-40 -mt-2" class="ArticleBoxImage flex justify-center items-center p-2 h-40 -mt-2"
in:scale={{ start: 0.8, duration: 500, delay: 100, easing: quintOut }} in:scale={{ start: 0.8, duration: 500, delay: 100, easing: quintOut }}
> >
{#if image} {#if image}
<LazyImage <LazyImage
src={image} src={image}
alt={title || "Publication image"} alt={title || "Publication image"}
eventId={event.id} eventId={event.id}
className="rounded w-full h-full object-cover" className="rounded w-full h-full object-cover"
/> />
{:else} {:else}
<div <div
class="rounded w-full h-full" class="rounded w-full h-full"
style="background-color: {generateDarkPastelColor(event.id)};" style="background-color: {generateDarkPastelColor(event.id)};"
> >
</div> </div>
{/if} {/if}
</div> </div>
<div class="flex flex-col space-y-4"> <div class="flex flex-col space-y-4">
<button onclick={() => showBlog()} class="text-left">
<h2 class="text-lg font-bold line-clamp-2" {title}>{title}</h2> <h2 class="text-lg font-bold line-clamp-2" {title}>{title}</h2>
</button> {#if hashtags}
{#if hashtags} <div class="tags">
<div class="tags"> {#each hashtags as tag}
{#each hashtags as tag} <span class="mr-2">#{tag}</span>
<span class="mr-2">#{tag}</span> {/each}
{/each} </div>
</div> {/if}
{/if} </div>
</div>
{#if active}
<Interactions {rootId} {event} /> <Interactions {rootId} {event} />
{/if}
<!-- Position CardActions at bottom-right -->
<!-- Position CardActions at bottom-right --> <div
<div class="absolute bottom-2 right-2"> class="absolute bottom-2 right-2 card-actions"
<CardActions {event} onDelete={handleDelete} /> role="button"
tabindex={0}
onclick={(e) => e.stopPropagation()}
onkeydown={(e) => e.stopPropagation()}
>
<CardActions {event} onDelete={handleDelete} />
</div>
</div>
</Card>
</div>
{:else}
<!-- Simple list view when collapsed -->
<div
class="py-2 pl-4 border-b border-gray-200 dark:border-gray-700 cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800 transition-colors"
role="button"
tabindex="0"
onclick={() => showBlog()}
onkeydown={(e) => {
if (e.key === 'Enter' || e.key === ' ') {
showBlog();
}
}}
>
<h3 class="text-base font-medium text-gray-900 dark:text-white">{title}</h3>
<div class="flex items-center gap-2 mt-1">
<p class="text-sm text-gray-500 dark:text-gray-400">{publishedAt()}</p>
<span class="text-sm text-gray-400 dark:text-gray-500"></span>
<p class="text-sm text-gray-500 dark:text-gray-400">{author}</p>
</div> </div>
</div> </div>
</Card> {/if}
{/if} {/if}

79
src/lib/components/publications/Publication.svelte

@ -240,11 +240,12 @@
} }
function loadBlog(rootId: string) { function loadBlog(rootId: string) {
// depending on the size of the screen, also toggle blog list & discussion visibility // depending on the size of the screen, also toggle discussion visibility
publicationColumnVisibility.update((current) => { publicationColumnVisibility.update((current) => {
const updated = current; const updated = current;
if (window.innerWidth < 1024) { if (window.innerWidth < 1024) {
updated.blog = false; // Don't set blog = false on mobile - we need it to show the article
// The blog list is already hidden via CSS (hidden md:flex)
updated.discussion = false; updated.discussion = false;
} }
updated.inner = true; updated.inner = true;
@ -659,38 +660,58 @@
</div> </div>
{/if} {/if}
<!-- Blog list --> <!-- Blog view: two-column layout on desktop, single column on mobile -->
{#if $publicationColumnVisibility.blog} {#if $publicationColumnVisibility.blog}
<!-- Remove overflow-auto --> <div class="flex flex-col md:flex-row gap-4 w-full">
<div <!-- Blog list: centered when no article, left when article is open -->
class={`flex flex-col p-4 space-y-4 max-w-xl flex-grow-1 ${isInnerActive() ? "discreet" : ""}`}
>
<div <div
class="card-leather bg-highlight dark:bg-primary-800 p-4 mb-4 rounded-lg border" class={`flex flex-col p-4 space-y-4 ${isInnerActive()
? 'md:flex-shrink-0 md:w-[500px]'
: 'mx-auto md:mx-auto max-w-3xl'} ${isInnerActive() ? 'hidden md:flex' : ''}`}
> >
<Details event={indexEvent} onDelete={handleDeletePublication} /> <div
class="card-leather bg-highlight dark:bg-primary-800 p-4 mb-4 rounded-lg border"
>
<Details event={indexEvent} onDelete={handleDeletePublication} />
</div>
<!-- List blog excerpts -->
{#each leaves as leaf, i}
{#if leaf}
<BlogHeader
rootId={leaf.tagAddress()}
event={leaf}
onBlogUpdate={loadBlog}
active={!isInnerActive()}
/>
{/if}
{/each}
</div> </div>
<!-- List blog excerpts -->
{#each leaves as leaf, i}
{#if leaf}
<BlogHeader
rootId={leaf.tagAddress()}
event={leaf}
onBlogUpdate={loadBlog}
active={!isInnerActive()}
/>
{/if}
{/each}
</div>
{/if}
{#if isInnerActive()} <!-- Selected article: right column on desktop, replaces TOC on mobile -->
{#key currentBlog} {#if isInnerActive()}
<!-- Remove overflow-auto & sticky; allow page scroll --> {#key currentBlog}
<div class="flex flex-col p-4 max-w-3xl flex-grow-2"> <div class="flex flex-col p-4 max-w-3xl md:flex-grow min-w-0 mx-auto md:mx-0">
<!-- ...existing code... --> {#if currentBlogEvent && currentBlog}
</div> {@const address = currentBlog}
{/key} <PublicationSection
{rootAddress}
{leaves}
{address}
{publicationTree}
{toc}
allComments={comments}
{commentsVisible}
ref={(el) => onPublicationSectionMounted(el, address)}
/>
{:else}
<div class="text-center py-8">
<p class="text-gray-500 dark:text-gray-400">Loading article...</p>
</div>
{/if}
</div>
{/key}
{/if}
</div>
{/if} {/if}
</div> </div>
</div> </div>

18
src/lib/components/util/ArticleNav.svelte

@ -93,6 +93,16 @@
}); });
} }
function handleBlogTocClick() {
if ($publicationColumnVisibility.inner) {
// Viewing article: go back to TOC
backToBlog();
} else if ($publicationColumnVisibility.blog) {
// Showing TOC: toggle it (though it should stay visible)
toggleColumn("blog");
}
}
function handleScroll() { function handleScroll() {
if (window.innerWidth < 768) { if (window.innerWidth < 768) {
const currentScrollY = window.scrollY; const currentScrollY = window.scrollY;
@ -160,11 +170,13 @@
<div class="flex items-center space-x-2 md:min-w-52 min-w-8"> <div class="flex items-center space-x-2 md:min-w-52 min-w-8">
{#if isIndexEvent} {#if isIndexEvent}
{#if publicationType === "blog"} {#if publicationType === "blog"}
<!-- Blog view: disabled when showing blog list, active when viewing article -->
<Button <Button
class={`btn-leather !w-auto ${$publicationColumnVisibility.blog ? "active" : ""}`} class={`btn-leather !w-auto ${$publicationColumnVisibility.inner ? "active" : ""}`}
outline={true} outline={true}
onclick={() => toggleColumn("blog")} disabled={$publicationColumnVisibility.blog && !$publicationColumnVisibility.inner}
title="Table of Contents" onclick={handleBlogTocClick}
title={$publicationColumnVisibility.inner ? "Back to Table of Contents" : "Table of Contents"}
> >
<BookOutline class="!fill-none" /> <BookOutline class="!fill-none" />
</Button> </Button>

Loading…
Cancel
Save