Browse Source

Tidied up and display rudimentary ToC. Updated screenshots.

master
Silberengel 10 months ago
parent
commit
1acd8b3acf
  1. 2
      src/lib/components/Preview.svelte
  2. 26
      src/lib/components/Publication.svelte
  3. 2
      src/lib/components/PublicationSection.svelte
  4. 4
      src/lib/components/util/ArticleNav.svelte
  5. 2
      src/lib/components/util/Interactions.svelte
  6. 66
      src/lib/components/util/TocToggle.svelte
  7. 5
      src/lib/parser.ts
  8. 3
      src/routes/new/compose/+page.svelte
  9. 3
      src/routes/new/edit/+page.svelte
  10. 15
      src/routes/start/+page.svelte
  11. BIN
      static/screenshots/ToC_blog.png
  12. BIN
      static/screenshots/ToC_normal.png

2
src/lib/components/Preview.svelte

@ -299,7 +299,7 @@ @@ -299,7 +299,7 @@
{/if}
<!-- Recurse on child indices and zettels -->
{#if publicationType === 'blog' && depth === 1}
<BlogHeader event={getBlogEvent(index)} rootId={rootId} onBlogUpdate={readBlog} />
<BlogHeader event={getBlogEvent(index)} rootId={rootId} onBlogUpdate={readBlog} active={true} />
{:else }
{#key subtreeUpdateCount}
{#each orderedChildren as id, index}

26
src/lib/components/Publication.svelte

@ -5,20 +5,14 @@ @@ -5,20 +5,14 @@
Card,
Sidebar,
SidebarGroup,
SidebarItem,
SidebarWrapper,
Skeleton,
TextPlaceholder,
Tooltip,
Heading,
} from "flowbite-svelte";
import { getContext, onDestroy, onMount } from "svelte";
import {
CloseOutline,
BookOutline,
ExclamationCircleOutline,
} from "flowbite-svelte-icons";
import { page } from "$app/state";
import type { NDKEvent } from "@nostr-dev-kit/ndk";
import PublicationSection from "./PublicationSection.svelte";
import type { PublicationTree } from "$lib/data_structures/publication_tree";
@ -27,6 +21,7 @@ @@ -27,6 +21,7 @@
import BlogHeader from "$components/blog/BlogHeader.svelte";
import Interactions from "$components/util/Interactions.svelte";
import TocToggle from "$components/util/TocToggle.svelte";
import { pharosInstance } from '$lib/parser';
let { rootAddress, publicationType, indexEvent } = $props<{
rootAddress: string;
@ -114,8 +109,10 @@ @@ -114,8 +109,10 @@
currentBlog = rootId;
// set current blog values for publication render
if (leaves.length > 0) {
currentBlogEvent =
leaves.find((i) => i.tagAddress() === currentBlog) ?? null;
leaves.find((i) => i && i.tagAddress() === currentBlog) ?? null;
}
}
function showBlogHeader() {
@ -156,11 +153,14 @@ @@ -156,11 +153,14 @@
observer.disconnect();
};
});
// Whenever the publication changes, update rootId
let rootId = $derived($pharosInstance.getRootIndexId());
</script>
<!-- Table of contents -->
{#if publicationType !== "blog" || !isLeaf}
<TocToggle rootId={rootAddress} />
<TocToggle {rootId} />
{/if}
<!-- Default publications -->
@ -171,7 +171,7 @@ @@ -171,7 +171,7 @@
>
<Details event={indexEvent} />
</div>
<!-- Publication -->
<!-- Publication sections/cards -->
{#each leaves as leaf, i}
{#if leaf == null}
<Alert class="flex space-x-2">
@ -215,12 +215,14 @@ @@ -215,12 +215,14 @@
</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}
@ -231,7 +233,7 @@ @@ -231,7 +233,7 @@
class="flex flex-col p-4 max-w-3xl overflow-auto flex-grow-2 max-h-[calc(100vh-146px)] sticky top-[146px]"
>
{#each leaves as leaf, i}
{#if leaf.tagAddress() === currentBlog}
{#if leaf && leaf.tagAddress() === currentBlog}
<div
class="card-leather bg-highlight dark:bg-primary-800 p-4 mb-4 rounded-lg border"
>
@ -245,7 +247,7 @@ @@ -245,7 +247,7 @@
ref={(el) => setLastElementRef(el, i)}
/>
<Card class="ArticleBox !hidden card-leather min-w-full grid mt-4">
<Card class="ArticleBox !hidden card-leather min-w-full mt-4">
<Interactions rootId={currentBlog} />
</Card>
{/if}
@ -273,7 +275,7 @@ @@ -273,7 +275,7 @@
alternative for other publications and
when blog is not opened, but discussion is opened from the list
-->
{#if showBlogHeader()}
{#if showBlogHeader() && currentBlog && currentBlogEvent}
<BlogHeader
rootId={currentBlog}
event={currentBlogEvent}

2
src/lib/components/PublicationSection.svelte

@ -104,7 +104,7 @@ @@ -104,7 +104,7 @@
});
</script>
<section bind:this={sectionRef} class='publication-leather content-visibility-auto'>
<section id={address} bind:this={sectionRef} class='publication-leather content-visibility-auto'>
{#await Promise.all([leafTitle, leafContent, leafHierarchy, publicationType, divergingBranches])}
<TextPlaceholder size='xxl' />
{:then [leafTitle, leafContent, leafHierarchy, publicationType, divergingBranches]}

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

@ -40,7 +40,7 @@ @@ -40,7 +40,7 @@
function shouldShowBack() {
const vis = $publicationColumnVisibility;
return ['discussion', 'toc', 'inner'].some(key => vis[key]);
return ['discussion', 'toc', 'inner'].some(key => vis[key as keyof typeof vis]);
}
function backToMain() {
@ -140,7 +140,7 @@ @@ -140,7 +140,7 @@
</Button>
{/if}
{#if publicationType !== 'blog' && !$publicationColumnVisibility.discussion}
<Button class="btn-leather !hidden hidden sm:flex !w-auto" outline={true} onclick={() => toggleColumn('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}

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

@ -80,7 +80,7 @@ @@ -80,7 +80,7 @@
}
</script>
<div class='InteractiveMenu !hidden flex flex-{direction} justify-around align-middle text-primary-700 dark:text-gray-500'>
<div class='InteractiveMenu !hidden flex-{direction} justify-around align-middle text-primary-700 dark:text-gray-500'>
<Button color="none" class='flex flex-{direction} shrink-0 md:min-w-11 min-h-11 items-center p-0' onclick={doLike}><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' onclick={doZap}><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' onclick={doHighlight}><FilePenOutline class="mx-2" size="lg"/><span>{highlightCount}</span></Button>

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

@ -1,18 +1,14 @@ @@ -1,18 +1,14 @@
<script lang="ts">
import {
Button, Heading,
Heading,
Sidebar,
SidebarGroup,
SidebarItem,
SidebarWrapper,
Skeleton,
TextPlaceholder,
Tooltip
} from "flowbite-svelte";
import { onMount } from "svelte";
import { pharosInstance } from "$lib/parser";
import { pharosInstance, tocUpdate } from "$lib/parser";
import { publicationColumnVisibility } from "$lib/stores";
import { page } from "$app/state";
let { rootId } = $props<{ rootId: string }>();
@ -22,7 +18,36 @@ @@ -22,7 +18,36 @@
const tocBreakpoint = 1140;
let activeHash = $state(page.url.hash);
let activeHash = $state(window.location.hash);
interface TocItem {
label: string;
hash: string;
}
// Get TOC items from parser
let tocItems = $state<TocItem[]>([]);
$effect(() => {
// This will re-run whenever tocUpdate changes
tocUpdate;
const items: TocItem[] = [];
const childIds = $pharosInstance.getChildIndexIds(rootId);
console.log('TOC rootId:', rootId, 'childIds:', childIds);
const processNode = (nodeId: string) => {
const title = $pharosInstance.getIndexTitle(nodeId);
if (title) {
items.push({
label: title,
hash: `#${nodeId}`
});
}
const children = $pharosInstance.getChildIndexIds(nodeId);
children.forEach(processNode);
};
childIds.forEach(processNode);
tocItems = items;
});
function normalizeHashPath(str: string): string {
return str
@ -48,11 +73,16 @@ @@ -48,11 +73,16 @@
}
}
function updateActiveHash() {
activeHash = window.location.hash;
}
/**
* Hides the table of contents sidebar when the window shrinks below a certain size. This
* prevents the sidebar from occluding the article content.
*/
function setTocVisibilityOnResize() {
// Always show TOC on laptop and larger screens, collapsible only on small/medium
publicationColumnVisibility.update(v => ({ ...v, toc: window.innerWidth >= tocBreakpoint }));
}
@ -66,7 +96,8 @@ @@ -66,7 +96,8 @@
return;
}
if ($publicationColumnVisibility.toc) {
// Only allow hiding TOC on screens smaller than tocBreakpoint
if (window.innerWidth < tocBreakpoint && $publicationColumnVisibility.toc) {
publicationColumnVisibility.update(v => ({ ...v, toc: false}));
}
}
@ -75,6 +106,7 @@ @@ -75,6 +106,7 @@
// Always check whether the TOC sidebar should be visible.
setTocVisibilityOnResize();
window.addEventListener("hashchange", updateActiveHash);
window.addEventListener("hashchange", scrollToElementWithOffset);
// Also handle the case where the user lands on the page with a hash in the URL
scrollToElementWithOffset();
@ -83,6 +115,7 @@ @@ -83,6 +115,7 @@
window.addEventListener("click", hideTocOnClick);
return () => {
window.removeEventListener("hashchange", updateActiveHash);
window.removeEventListener("hashchange", scrollToElementWithOffset);
window.removeEventListener("resize", setTocVisibilityOnResize);
window.removeEventListener("click", hideTocOnClick);
@ -92,17 +125,18 @@ @@ -92,17 +125,18 @@
<!-- TODO: Get TOC from parser. -->
{#if $publicationColumnVisibility.toc}
<Sidebar class='sidebar-leather left-0' {activeHash}>
<Sidebar class='sidebar-leather left-0'>
<SidebarWrapper>
<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}-->
<p>(This ToC is only for demo purposes, and is not fully-functional.)</p>
{#each tocItems as item}
<SidebarItem
class="sidebar-item-leather {activeHash === item.hash ? 'bg-primary-200 font-bold' : ''}"
label={item.label}
href={item.hash}
/>
{/each}
</SidebarGroup>
</SidebarWrapper>
</Sidebar>

5
src/lib/parser.ts

@ -1114,3 +1114,8 @@ export default class Pharos { @@ -1114,3 +1114,8 @@ export default class Pharos {
}
export const pharosInstance: Writable<Pharos> = writable();
export const tocUpdate = writable(0);
// Whenever you update the publication tree, call:
tocUpdate.update(n => n + 1);

3
src/routes/new/compose/+page.svelte

@ -5,6 +5,7 @@ @@ -5,6 +5,7 @@
let treeNeedsUpdate: boolean = false;
let treeUpdateCount: number = 0;
let someIndexValue = 0;
$: {
if (treeNeedsUpdate) {
@ -17,7 +18,7 @@ @@ -17,7 +18,7 @@
<main class='main-leather flex flex-col space-y-4 max-w-2xl w-full mt-4 mb-4'>
<Heading tag='h1' class='h-leather mb-2'>Compose</Heading>
{#key treeUpdateCount}
<Preview rootId={$pharosInstance.getRootIndexId()} allowEditing={true} bind:needsUpdate={treeNeedsUpdate} />
<Preview rootId={$pharosInstance.getRootIndexId()} allowEditing={true} bind:needsUpdate={treeNeedsUpdate} index={someIndexValue} />
{/key}
</main>
</div>

3
src/routes/new/edit/+page.svelte

@ -5,6 +5,7 @@ @@ -5,6 +5,7 @@
import Pharos, { pharosInstance } from "$lib/parser";
import { ndkInstance } from "$lib/ndk";
import { goto } from "$app/navigation";
let someIndexValue = 0;
// TODO: Prompt user to sign in before editing.
@ -80,7 +81,7 @@ @@ -80,7 +81,7 @@
</ToolbarButton>
</Toolbar>
{#if rootIndexId}
<Preview sectionClass='m-2' rootId={rootIndexId} />
<Preview sectionClass='m-2' rootId={rootIndexId} index={someIndexValue} />
{/if}
</form>
{/if}

15
src/routes/start/+page.svelte

@ -53,20 +53,23 @@ @@ -53,20 +53,23 @@
<P class="mb-3">
Each content section (30041 or 30818) is also a level in the table of
contents, which can be accessed from the floating icon top-left in the
reading view. This allows for navigation within the publication. (This
functionality has been temporarily disabled.)
reading view. This allows for navigation within the publication.
Publications of type "blog" have a ToC which emphasizes that each entry
is a blog post.
(This functionality has been temporarily disabled, but the TOC is visible.)
</P>
<div class="flex flex-col items-center space-y-4 my-4">
<Img
src="/screenshots/ToC_icon.png"
alt="ToC icon"
src="/screenshots/ToC_normal.png"
alt="ToC basic"
class="image-border rounded-lg"
width="400"
/>
<Img
src="/screenshots/TableOfContents.png"
alt="Table of contents example"
src="/screenshots/ToC_blog.png"
alt="ToC blog"
class="image-border rounded-lg"
width="400"
/>

BIN
static/screenshots/ToC_blog.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 385 KiB

BIN
static/screenshots/ToC_normal.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 205 KiB

Loading…
Cancel
Save