Browse Source

Refactored visibility store, changed styles yet again...

master
Nuša Pukšič 10 months ago
parent
commit
82f0d20e23
  1. 7
      src/app.css
  2. 140
      src/lib/components/Publication.svelte
  3. 93
      src/lib/components/util/ArticleNav.svelte
  4. 15
      src/lib/components/util/Interactions.svelte
  5. 51
      src/lib/components/util/TocToggle.svelte
  6. 20
      src/lib/stores.ts

7
src/app.css

@ -159,8 +159,13 @@ @@ -159,8 +159,13 @@
}
/* Sidebar */
aside.sidebar-leather {
@apply fixed md:sticky top-[130px] sm:top-[146px] h-[calc(100vh-130px)] sm:h-[calc(100vh-146px)] z-10;
@apply bg-primary-0 dark:bg-primary-1000 px-5 w-full sm:w-auto sm:max-w-xl;
}
aside.sidebar-leather > div {
@apply bg-gray-100 dark:bg-gray-900;
@apply bg-primary-50 dark:bg-primary-900 h-full px-5 py-0;
}
a.sidebar-item-leather {

140
src/lib/components/Publication.svelte

@ -7,9 +7,9 @@ @@ -7,9 +7,9 @@
SidebarWrapper,
Skeleton,
TextPlaceholder,
Tooltip
Tooltip, Heading
} from "flowbite-svelte";
import { HeartOutline } from 'flowbite-svelte-icons';
import { CloseOutline } from 'flowbite-svelte-icons';
import { getContext, onDestroy, onMount } from "svelte";
import type { NDKEvent } from "@nostr-dev-kit/ndk";
import PublicationSection from "./PublicationSection.svelte";
@ -18,7 +18,7 @@ @@ -18,7 +18,7 @@
import { publicationColumnVisibility } from "$lib/stores";
import BlogHeader from "$components/blog/BlogHeader.svelte";
import Interactions from "$components/util/Interactions.svelte";
import ZapOutline from "$components/util/ZapOutline.svelte";
import TocToggle from "$components/util/TocToggle.svelte";
let { rootAddress, publicationType, indexEvent } = $props<{
rootAddress: string,
@ -70,42 +70,49 @@ @@ -70,42 +70,49 @@
// #endregion
// region Columns visibility
let currentBlog: null|string = $state(null);
let currentBlogEvent: null|NDKEvent = $state(null);
function isDefaultVisible() {
if (publicationType !== 'blog') {
return true;
} else {
return $publicationColumnVisibility.blog;
}
}
function isInnerActive() {
return currentBlog !== null && $publicationColumnVisibility.inner;
}
function isSocialActive() {
return $publicationColumnVisibility.discussion;
function closeDiscussion() {
publicationColumnVisibility.update(v => ({ ...v, discussion: false}));
}
function loadBlog(rootId: string) {
// depending on the size of the screen, also toggle blog list & social visibility
// depending on the size of the screen, also toggle blog list & discussion visibility
publicationColumnVisibility.update(current => {
const updated = current;
if (window.innerWidth < 1024) {
$publicationColumnVisibility.blog = false;
$publicationColumnVisibility.discussion = false;
updated.blog = false;
updated.discussion = false;
}
$publicationColumnVisibility.inner = true;
updated.inner = true;
return updated;
});
currentBlog = rootId;
// set current blog values for publication render
currentBlogEvent = leaves.find(i => i.tagAddress() === currentBlog) ?? null;
}
function showBlogHeaderOnMobile() {
function showBlogHeader() {
return (currentBlog && currentBlogEvent && window.innerWidth < 1140);
}
onDestroy(() => {
// reset visibility
publicationColumnVisibility.reset();
})
onMount(() => {
// Set current columns depending on the publication type
const isBlog = publicationType === 'blog';
publicationColumnVisibility.update(v => ({ ...v, main: !isBlog, blog: isBlog }));
// Set up the intersection observer.
observer = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
@ -121,30 +128,39 @@ @@ -121,30 +128,39 @@
};
});
onDestroy(() => {
// reset visibility
$publicationColumnVisibility = {
toc: false,
blog: true,
main: true,
inner: true,
discussion: false,
editing: false
};
})
</script>
{#if isDefaultVisible()}
<div class="flex flex-col p-4 space-y-4 overflow-auto
{publicationType === 'blog' ? 'max-w-xl flex-grow-1' : 'max-w-2xl flex-grow-2' }
<!-- Table of contents -->
{#if publicationType !== 'blog'}
<TocToggle rootId={rootAddress} />
{/if}
<!-- Default publications -->
{#if $publicationColumnVisibility.main}
<div class="flex flex-col p-4 space-y-4 overflow-auto max-w-2xl flex-grow-2">
<div class="card-leather bg-highlight dark:bg-primary-800 p-4 mb-4 rounded-lg border">
<Details event={indexEvent} />
</div>
<!-- Publication -->
{#each leaves as leaf, i}
<PublicationSection
rootAddress={rootAddress}
leaves={leaves}
address={leaf.tagAddress()}
ref={(el) => setLastElementRef(el, i)}
/>
{/each}
</div>
{/if}
<!-- Blog list -->
{#if $publicationColumnVisibility.blog}
<div class="flex flex-col p-4 space-y-4 overflow-auto max-w-xl flex-grow-1
{isInnerActive() ? 'discreet' : ''}
">
<div class="card-leather bg-highlight dark:bg-primary-800 p-4 mb-4 rounded-lg border">
<Details event={indexEvent} />
</div>
{#if publicationType === 'blog'}
<!-- List blog excerpts -->
{#each leaves as leaf, i}
<BlogHeader
@ -154,17 +170,6 @@ @@ -154,17 +170,6 @@
active={!(isInnerActive())}
/>
{/each}
{:else}
{#each leaves as leaf, i}
<PublicationSection
rootAddress={rootAddress}
leaves={leaves}
address={leaf.tagAddress()}
ref={(el) => setLastElementRef(el, i)}
/>
{/each}
{/if}
</div>
{/if}
@ -193,16 +198,28 @@ @@ -193,16 +198,28 @@
{/key}
{/if}
{#if isSocialActive() }
<div class="flex flex-col p-4 max-w-3xl overflow-auto flex-grow-2 h-[calc(100vh-146px)] sticky top-[146px] space-y-4">
{#if showBlogHeaderOnMobile()}
{#if $publicationColumnVisibility.discussion }
<Sidebar class='sidebar-leather right-0 md:!pl-8'>
<SidebarWrapper>
<SidebarGroup class='sidebar-group-leather'>
<div class="flex justify-between items-baseline">
<Heading tag="h1" class="h-leather !text-lg">Discussion</Heading>
<Button class="btn-leather hidden sm:flex z-30 !p-1 bg-primary-50 dark:bg-primary-900" outline onclick={closeDiscussion}>
<CloseOutline />
</Button>
</div>
<div class="flex flex-col space-y-4">
<!-- TODO
alternative for other publications and
when blog is not opened, but discussion is opened from the list
-->
{#if showBlogHeader()}
<BlogHeader
rootId={currentBlog}
event={currentBlogEvent}
onBlogUpdate={loadBlog}
active={true}
/>
{/if}
<div class="flex flex-col w-full space-y-4">
<Card class="ArticleBox card-leather w-full grid max-w-xl">
@ -214,26 +231,9 @@ @@ -214,26 +231,9 @@
This is a very intelligent comment placeholder that applies to all the content equally well.
</div>
</Card>
<Card class="ArticleBox card-leather w-full grid grid-cols-2 max-w-xl">
<div class="flex flex-col my-2">
<span>Unknown</span>
<span class='text-gray-500'>1.1.1970</span>
</div>
<div class='flex flex-col flex-grow items-end justify-center'>
<ZapOutline ></ZapOutline>
</div>
</Card>
<Card class="ArticleBox card-leather w-full grid grid-cols-2 max-w-xl">
<div class="flex flex-col my-2">
<span>Unknown</span>
<span class='text-gray-500'>1.1.1970</span>
</div>
<div class='flex flex-col flex-grow items-end justify-center'>
<HeartOutline />
</div>
</Card>
</div>
</div>
</SidebarGroup>
</SidebarWrapper>
</Sidebar>
{/if}

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

@ -1,6 +1,5 @@ @@ -1,6 +1,5 @@
<script lang="ts">
import TocToggle from "$components/util/TocToggle.svelte";
import { BookOutline, CaretLeftOutline, GlobeOutline } from "flowbite-svelte-icons";
import { BookOutline, CaretLeftOutline, CloseOutline, GlobeOutline } from "flowbite-svelte-icons";
import { Button } from "flowbite-svelte";
import { publicationColumnVisibility } from "$lib/stores";
import InlineProfile from "$components/util/InlineProfile.svelte";
@ -13,8 +12,7 @@ @@ -13,8 +12,7 @@
} = $props<{
rootId: any,
publicationType: string,
indexEvent: NDKEvent,
blogEvent: NDKEvent|null
indexEvent: NDKEvent
}>();
let title: string = $derived(indexEvent.getMatchingTags('title')[0]?.[1]);
@ -22,68 +20,95 @@ @@ -22,68 +20,95 @@
let pubkey: string = $derived(indexEvent.getMatchingTags('p')[0]?.[1] ?? null);
// Function to toggle column visibility
function toggleColumn(column: 'blog'|'inner'|'discussion') {
publicationColumnVisibility.update(store => {
store[column] = !store[column]; // Toggle true/false
if (window.innerWidth < 1400 && column === 'discussion' && $publicationColumnVisibility.discussion) {
$publicationColumnVisibility.blog = false;
}
if (window.innerWidth < 1400 && column === 'blog' && $publicationColumnVisibility.blog) {
$publicationColumnVisibility.discussion = false;
}
if (window.innerWidth < 980) {
$publicationColumnVisibility.inner = false;
function toggleColumn(column: 'toc' | 'blog' | 'inner' | 'discussion') {
publicationColumnVisibility.update(current => {
const newValue = !current[column];
const updated = { ...current, [column]: newValue };
if (window.innerWidth < 1400 && column === 'blog' && newValue) {
updated.discussion = false;
}
return { ...store }; // Ensure reactivity
return updated;
});
}
function shouldShowBack() {
const vis = $publicationColumnVisibility;
return ['discussion', 'toc', 'inner'].some(key => vis[key]);
}
function backToMain() {
if ($publicationColumnVisibility.discussion) {
$publicationColumnVisibility.inner = true;
$publicationColumnVisibility.discussion = false;
publicationColumnVisibility.update(current => {
const updated = { ...current };
// if current is 'inner', just go back to blog
if (current.inner && !(current.discussion || current.toc)) {
updated.inner = false;
updated.blog = true;
return updated;
}
updated.discussion = false;
updated.toc = false;
if (publicationType === 'blog') {
updated.inner = true;
updated.blog = false;
} else {
$publicationColumnVisibility.blog = true;
$publicationColumnVisibility.inner = false;
$publicationColumnVisibility.discussion = false;
updated.main = true;
}
return updated;
});
}
function backToBlog() {
publicationColumnVisibility.update(current => {
const updated = { ...current };
updated.inner = false;
updated.discussion = false;
updated.blog = true;
return updated;
})
}
</script>
<nav class="Navbar navbar-leather flex fixed top-[76px] w-full min-h-[70px] px-2 sm:px-4 py-2.5 z-10">
<nav class="Navbar navbar-leather flex fixed top-[60px] sm:top-[76px] w-full min-h-[70px] px-2 sm:px-4 py-2.5 z-10">
<div class="mx-auto flex space-x-2 container">
<div class="flex items-center space-x-2 md:min-w-52 min-w-8">
{#if publicationType === 'blog'}
{#if $publicationColumnVisibility.inner || $publicationColumnVisibility.discussion}
{#if shouldShowBack()}
<Button class='btn-leather !w-auto sm:hidden' outline={true} onclick={backToMain}>
<CaretLeftOutline class="!fill-none inline mr-1" /><span class="hidden sm:inline">Back</span>
</Button>
{/if}
{#if publicationType === 'blog'}
<Button class="btn-leather hidden sm:flex !w-auto {$publicationColumnVisibility.blog ? 'active' : ''}"
outline={true} onclick={() => toggleColumn('blog')} >
<BookOutline class="!fill-none inline mr-1" /><span class="hidden sm:inline">Table of Contents</span>
</Button>
{:else}
<TocToggle rootId={rootId} />
{#if !$publicationColumnVisibility.discussion && !$publicationColumnVisibility.toc}
<Button class='btn-leather !w-auto' outline={true} onclick={() => toggleColumn('toc')}>
<BookOutline class="!fill-none inline mr-1" /><span class="hidden sm:inline">Table of Contents</span>
</Button>
{/if}
{/if}
</div>
<div class="flex flex-grow text justify-center items-center">
<p class="max-w-[60vw] line-ellipsis"><b class="text-nowrap">{title}</b> <span class="whitespace-nowrap">by <InlineProfile pubkey={pubkey} title={author} /></span></p>
</div>
<div class="flex items-center space-x-2 md:min-w-52 min-w-8">
{#if publicationType === 'blog'}
{#if $publicationColumnVisibility.inner || $publicationColumnVisibility.discussion}
<Button class='btn-leather !w-auto hidden sm:flex' outline={true} onclick={backToMain}>
<CaretLeftOutline class="!fill-none inline mr-1" /><span class="hidden sm:inline">Back</span>
<div class="flex justify-end space-x-2 md:min-w-52 min-w-8">
{#if $publicationColumnVisibility.inner}
<Button class='btn-leather !w-auto hidden sm:flex' outline={true} onclick={backToBlog}>
<CloseOutline class="!fill-none inline mr-1" /><span class="hidden sm:inline">Close</span>
</Button>
{/if}
{#if $publicationColumnVisibility.inner}
<Button class='btn-leather !w-auto' outline={true} onclick={() => toggleColumn('discussion')}>
{#if publicationType !== 'blog' && !$publicationColumnVisibility.discussion}
<Button class="btn-leather hidden sm:flex !w-auto" outline={true} onclick={() => toggleColumn('discussion')} >
<GlobeOutline class="!fill-none inline mr-1" /><span class="hidden sm:inline">Discussion</span>
</Button>
{/if}
{/if}
</div>
</div>
</nav>

15
src/lib/components/util/Interactions.svelte

@ -56,12 +56,15 @@ @@ -56,12 +56,15 @@
subs.push(subscribeCount(1, comments)); // comments (Text Notes)
});
function showSocial() {
$publicationColumnVisibility.discussion = true;
if (window.innerWidth < 1400) {
$publicationColumnVisibility.blog = false;
$publicationColumnVisibility.inner = false;
function showDiscussion() {
publicationColumnVisibility.update(v => {
const updated = { ...v, discussion: true};
// hide blog, unless the only column
if (v.inner) {
updated.blog = (v.blog && window.innerWidth >= 1400 );
}
return updated;
});
}
</script>
@ -69,5 +72,5 @@ @@ -69,5 +72,5 @@
<Button color="none" class='flex flex-{direction} shrink-0 md:min-w-11 min-h-11 items-center p-0'><HeartOutline class="mx-2" size="lg" /><span>{likeCount}</span></Button>
<Button color="none" class='flex flex-{direction} shrink-0 md:min-w-11 min-h-11 items-center p-0'><ZapOutline className="mx-2" /><span>{zapCount}</span></Button>
<Button color="none" class='flex flex-{direction} shrink-0 md:min-w-11 min-h-11 items-center p-0'><FilePenOutline class="mx-2" size="lg"/><span>{highlightCount}</span></Button>
<Button color="none" class='flex flex-{direction} shrink-0 md:min-w-11 min-h-11 items-center p-0' onclick={() => showSocial()}><AnnotationOutline class="mx-2" size="lg"/><span>{commentCount}</span></Button>
<Button color="none" class='flex flex-{direction} shrink-0 md:min-w-11 min-h-11 items-center p-0' onclick={showDiscussion}><AnnotationOutline class="mx-2" size="lg"/><span>{commentCount}</span></Button>
</div>

51
src/lib/components/util/TocToggle.svelte

@ -1,17 +1,17 @@ @@ -1,17 +1,17 @@
<script lang="ts">
import {
Button,
Button, Heading,
Sidebar,
SidebarGroup,
SidebarItem,
SidebarWrapper,
Skeleton,
TextPlaceholder,
Tooltip,
Tooltip
} from "flowbite-svelte";
import { onMount } from "svelte";
import { BookOutline } from "flowbite-svelte-icons";
import { pharosInstance } from "$lib/parser";
import { publicationColumnVisibility } from "$lib/stores";
import { page } from "$app/state";
let { rootId } = $props<{ rootId: string }>();
@ -23,8 +23,6 @@ @@ -23,8 +23,6 @@
const tocBreakpoint = 1140;
let activeHash = $state(page.url.hash);
let showToc: boolean = $state(true);
let showTocButton: boolean = $state(false);
function normalizeHashPath(str: string): string {
return str
@ -55,8 +53,7 @@ @@ -55,8 +53,7 @@
* prevents the sidebar from occluding the article content.
*/
function setTocVisibilityOnResize() {
showToc = window.innerWidth >= tocBreakpoint;
showTocButton = window.innerWidth < tocBreakpoint;
publicationColumnVisibility.update(v => ({ ...v, toc: window.innerWidth >= tocBreakpoint}));
}
/**
@ -69,8 +66,8 @@ @@ -69,8 +66,8 @@
return;
}
if (showToc) {
showToc = false;
if ($publicationColumnVisibility.toc) {
publicationColumnVisibility.update(v => ({ ...v, toc: false}));
}
}
@ -93,32 +90,20 @@ @@ -93,32 +90,20 @@
});
</script>
{#if showTocButton && !showToc}
<Button
class="btn-leather !w-auto"
outline={true}
on:click={(ev) => {
showToc = true;
ev.stopPropagation();
}}
>
<BookOutline class="!fill-none mr-1"/>
<span class="hidden sm:flex">Table of Contents</span>
</Button>
{/if}
<!-- TODO: Get TOC from parser. -->
<!-- {#if showToc}
<Sidebar class='sidebar-leather fixed top-20 left-0 px-4 w-60' {activeHash}>
{#if $publicationColumnVisibility.toc}
<Sidebar class='sidebar-leather left-0' {activeHash}>
<SidebarWrapper>
<SidebarGroup class='sidebar-group-leather overflow-y-scroll'>
{#each events as event}
<SidebarItem
class='sidebar-item-leather'
label={event.getMatchingTags('title')[0][1]}
href={`${$page.url.pathname}#${normalizeHashPath(event.getMatchingTags('title')[0][1])}`}
/>
{/each}
<SidebarGroup class='sidebar-group-leather'>
<Heading tag="h1" class="h-leather !text-lg">Table of contents</Heading>
<!--{#each events as event}-->
<!-- <SidebarItem-->
<!-- class='sidebar-item-leather'-->
<!-- label={event.getMatchingTags('title')[0][1]}-->
<!-- href={`${$page.url.pathname}#${normalizeHashPath(event.getMatchingTags('title')[0][1])}`}-->
<!-- />-->
<!--{/each}-->
</SidebarGroup>
</SidebarWrapper>
</Sidebar>
{/if} -->
{/if}

20
src/lib/stores.ts

@ -7,11 +7,25 @@ export let alexandriaKinds = readable<number[]>([30040, 30041, 30818]); @@ -7,11 +7,25 @@ export let alexandriaKinds = readable<number[]>([30040, 30041, 30818]);
export let feedType = writable<FeedType>(FeedType.StandardRelays);
export const publicationColumnVisibility = writable({
const defaultVisibility = {
toc: false,
blog: true,
main: true,
inner: true,
inner: false,
discussion: false,
editing: false
});
};
function createVisibilityStore() {
const { subscribe, set, update } = writable({ ...defaultVisibility });
return {
subscribe,
set,
update,
reset: () => set({ ...defaultVisibility })
};
}
export const publicationColumnVisibility = createVisibilityStore();

Loading…
Cancel
Save