Skip to content

Commit

Permalink
Merge pull request #105 from tjheffner/lazyload
Browse files Browse the repository at this point in the history
Update lazyload, add TOC
  • Loading branch information
tjheffner authored Jan 27, 2024
2 parents 5314eae + 2192de4 commit cdd8dff
Show file tree
Hide file tree
Showing 30 changed files with 169 additions and 69 deletions.
17 changes: 17 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,15 @@
"postinstall": "svelte-kit sync",
"check": "svelte-check --tsconfig ./tsconfig.json",
"check:watch": "svelte-check --tsconfig ./tsconfig.json --watch",
"lint": "prettier --check --plugin-search-dir=. . && eslint --ignore-path .gitignore .",
"format": "prettier --write --plugin-search-dir=. ."
"lint": "prettier --check . && eslint --ignore-path .gitignore .",
"format": "prettier --write ."
},
"devDependencies": {
"@resvg/resvg-js": "^2.4.1",
"@sveltejs/adapter-auto": "^3.1.1",
"@sveltejs/adapter-netlify": "^2.0.8",
"@sveltejs/kit": "1.27.6",
"@svelte-put/toc": "^4.0.0",
"@tailwindcss/typography": "^0.5.10",
"@typescript-eslint/eslint-plugin": "^4.33.0",
"@typescript-eslint/parser": "^4.33.0",
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
10 changes: 5 additions & 5 deletions src/lib/components/EmojiWall.svelte
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
<script>
const WALL_SIZE = 84
const WALL_SIZE = 96
// The first emoji in the array becomes the dominant background
// The others intersperse randomly
const emojis = [
['🌊','🦑','🦀','🐳','🦐','🐡','🐠','🦈','🏄‍♂️','🏝'],
['🌳','🦥','🦜','🐊','🐸','🐒','🌺','🍄','🐯','🐜'],
['🏜','🌵','🌞','🦂','🐍','🤠','🦇','🦉','🐫'],
['🌊','🦑','🦀','🐳','🦐','🐡','🐠','🦈','🏄‍♂️','🏝','🦞','🐋'],
['🌳','🦥','🦜','🐊','🐸','🐒','🌺','🍄','🐯','🐜','🦟','🐝'],
['🏜','🌵','🌞','🦂','🐍','🤠','🦇','🦉','🐫', '🦗'],
['🏔','🏕','🌲','🦅','🌝','🏂']
]
Expand Down Expand Up @@ -52,7 +52,7 @@
let visible = shuffleArray(scenes[initial])
</script>

<div class="block text-shadow text-4xl md:text-6xl mt-4 lg:mt-8 w-full max-h-[400px] md:max-h-[450px] overflow-hidden lg:overflow-visible" aria-hidden="true">
<div class="block text-shadow text-4xl md:text-6xl mt-4 p-8 lg:mt-8 w-full max-h-[370px] md:max-h-[415px] overflow-hidden" aria-hidden="true">
{#each visible as f, i}
<!-- <span>{i}</span> -->
<button
Expand Down
2 changes: 1 addition & 1 deletion src/lib/components/PostItem.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
<span
class="
w-fit rounded-full
bg-alternate dark:bg-secondary px-2
bg-alternate px-2
py-1 text-xs font-bold
text-red-600 text-white md:px-4 dark:text-gray-900
">
Expand Down
11 changes: 9 additions & 2 deletions src/lib/components/Slice.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@
export let prose = true
export let full = false
export let warn = false
export let titleLink = true
let tag = 'a'
if (titleLink === false) {
tag = 'div'
}
let sliceClasses = "slice mb-6 grid w-full grid-cols-4 gap-4 py-8 md:mb-8 md:gap-8 md:py-12 lg:gap-12 xl:gap-16"
let slotWrapperClasses = 'w-full md:w-2/3 md:ml-4 lg:ml-12'
Expand All @@ -24,9 +31,9 @@
{#if title}
<div class="col-span-4 md:col-span-1">
<h2 class="sticky top-4 mb-2 text-xl font-bold text-black md:ml-4 md:text-2xl lg:ml-12 dark:text-blue-300">
<a href="#{title}" class="slice-title">
<svelte:element this={tag} href={titleLink ? `#${title}` : null} class="slice-title">
{title}{#if warn}<a href="#warning" class="text-secondary">*</a>{/if}
</a>
</svelte:element>
{#if date}
<span class="text-unshadow text-base text-slate-500 dark:text-gray-600">
({date.slice(0, 4)})
Expand Down
58 changes: 58 additions & 0 deletions src/lib/components/Toc.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<script>
import { onMount } from "svelte";
export let tocStore;
let isOpen = false;
onMount(() => {
// set isOpen if window width is mobile checking the media query
if (window.matchMedia("(min-width: 640px)").matches) {
isOpen = true;
}
})
console.log($tocStore.items)
</script>

<!-- Table of contents thing -->
{#if Object.values($tocStore.items).length && Object.values($tocStore.items).length > 1}
<section
class="sticky max-w-[12em] top-10 h-0 z-10"
>
<button
class="text-accent font-bold -ml-1 lg:ml-7"
aria-label="{isOpen ? 'Close' : 'Open'} Table of Contents"
on:click={() => (isOpen = !isOpen)}
>
{isOpen ? '<' : '>'}
</button>

<!-- adjust margins to slide in/out, works with sticky -->
<!-- need to adjust left margin to slide text, right margin to slide bg -->
<ul class="toc-list max-h-fit {isOpen ? '-ml-6 mr-0 lg:ml-2' : '-ml-96 mr-96'}">
{#each Object.values($tocStore.items) as { id, text, element }}
<li>
<a
class="ml-2 block text-sm"
class:toc-active={$tocStore.activeItem?.id === id}
class:pl-2={element.nodeName === 'H2' || element.nodeName === 'H1'}
class:pl-4={element.nodeName === 'H3'}
href="#{id}"
>
{text}
</a>
</li>
{/each}
</ul>
</section>
{/if}

<style>
.toc-list {
@apply text-secondary py-5 px-8 -mt-10 space-y-1 overflow-auto transition-all duration-500;
@apply bg-orange-100 dark:bg-slate-900;
}
.toc-active {
@apply text-accent font-bold border-s-2 border-current;
/* margin-left:; */
}
</style>
2 changes: 1 addition & 1 deletion src/lib/content.js
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ function parseIssue(issue, label) {
...base,
alt: data.alt,
images: data.images,
warn: data.warn || false
warn: data.warn || false,
}
break
case 'Published':
Expand Down
58 changes: 28 additions & 30 deletions src/lib/loadUtterances.js
Original file line number Diff line number Diff line change
@@ -1,57 +1,55 @@

// this is an action that loads utterances comments when the element is in view
// so as not to incur the JS cost upfront if the person never scrolls down to the comments

// https://svelte.dev/repl/c6a402704224403f96a3db56c2f48dfc?version=3.55.1

import { GH_USER_REPO } from "$lib/siteConfig";
import { GH_USER_REPO } from '$lib/siteConfig'

/** @type any */
let intersectionObserver;
let intersectionObserver

let hasLoaded = false

export function injectScript(element, number) {
// have to do this because direct injection using @html doesnt work
// adapted from https://github.com/utterance/utterances/issues/161#issuecomment-550991248
const scriptElem = document.createElement("script");
scriptElem.src = "https://utteranc.es/client.js";
scriptElem.async = true;
scriptElem.crossOrigin = "anonymous";
scriptElem.setAttribute("repo", GH_USER_REPO);
scriptElem.setAttribute("issue-number", number);
const theme = document.documentElement.classList.contains('dark') ? 'github-dark-orange' : 'boxy-light';
scriptElem.setAttribute("theme", theme);
const scriptElem = document.createElement('script')
scriptElem.src = 'https://utteranc.es/client.js'
scriptElem.async = true
scriptElem.crossOrigin = 'anonymous'
scriptElem.setAttribute('repo', GH_USER_REPO)
scriptElem.setAttribute('issue-number', number)
const theme = document.documentElement.classList.contains('dark')
? 'github-dark-orange'
: 'boxy-light'
scriptElem.setAttribute('theme', theme)

// replace all contents of element and append script
element.innerHTML = "";
element.appendChild(scriptElem);
element.innerHTML = ''
element.appendChild(scriptElem)
}

export default function viewport(element, { number }) {

function ensureIntersectionObserver() {
if (intersectionObserver) return;

intersectionObserver = new IntersectionObserver(
(entries) => {
entries.forEach(entry => {
if (!hasLoaded && entry.isIntersecting) {
injectScript(element, number);
hasLoaded = true
}
});
}
);
if (intersectionObserver) return

intersectionObserver = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
if (!hasLoaded && entry.isIntersecting) {
injectScript(element, number)
hasLoaded = true
}
})
})
}
ensureIntersectionObserver();
ensureIntersectionObserver()

intersectionObserver.observe(element);
intersectionObserver.observe(element)

return {
destroy() {
hasLoaded = false
intersectionObserver.unobserve(element);
}
intersectionObserver.unobserve(element)
},
}
}
2 changes: 1 addition & 1 deletion src/lib/localContent.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ let localContent = []

// fetch all markdown posts and shape into item with metadata
export const fetchMarkdownPosts = async () => {
const allPostFiles = import.meta.glob('$lib/content/work/*.svx')
const allPostFiles = import.meta.glob('./../content/work/*.svx')
const iterablePostFiles = Object.entries(allPostFiles)

const allPosts = await Promise.all(
Expand Down
7 changes: 6 additions & 1 deletion src/lib/rehype-wrap-img.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,12 @@ export default function rehypeZoomImages(options = { selector: 'img' }) {
const label = fromSelector('label')
const checkbox = fromSelector('input[type="checkbox"]')

node.properties.loading = "lazy"
node.properties.loading = 'lazy'

// needs height and width defined for lazy loading
node.properties.height = '100%'
node.properties.width = '100%'
node.properties.class = 'lazy-image'

label.children = [checkbox, node]
wrapper.children = [label]
Expand Down
6 changes: 3 additions & 3 deletions src/lib/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import rehypeSlug from 'rehype-slug'
import rehypeAutoLink from 'rehype-autolink-headings'
import rehypeZoomImages from './rehype-wrap-img.js'

const remarkPlugins = [ remarkUnwrapImages ]
const remarkPlugins = [remarkUnwrapImages]
const rehypePlugins = [
rehypeStringify,
rehypeSlug,
Expand Down Expand Up @@ -180,8 +180,8 @@ export async function formatContent(content) {
// https://github.com/pngwn/MDsveX/issues/392
.replace(/>{@html `<code class="language-/g, '><code class="language-')
.replace(/<\/code>`}<\/pre>/g, '</code></pre>')
// lazy load images
// .replace(/<img/g, '<img loading="lazy" ')
// lazy load images
// .replace(/<img/g, '<img loading="lazy" ')

return output
}
2 changes: 1 addition & 1 deletion src/params/string.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// test that a route doesn't end in .xml
// so [slug] can greedy match everything except .xml routes
export function match(value) {
return /^(?!.*[.]xml$).*$/.test(value)
return /^(?!.*[.]xml$).*$/.test(value)
}
2 changes: 1 addition & 1 deletion src/routes/(main)/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@
<a href="/about">about</a>.
</p>
<p />
<p>Thanks for stopping by, check out the links that work. ✌️</p>
<p>Thanks for stopping by, check out the <a href="/blogroll">links that work</a>. ✌️</p>
</Slice>

<Slice title="Currently...">
Expand Down
17 changes: 9 additions & 8 deletions src/routes/(main)/gallery/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@
<meta name="description" content="part photo gallery, part adventure log" />
<meta
property="og:image"
content={`https://heffner.dev/og?message=adventure%20log`}
content={`https://heffner.dev/og?message=photo%20gallery`}
/>
<meta name="twitter:card" content={'summary'} />
<meta
name="twitter:image"
content={`https://heffner.dev/og?message=adventure%20log`}
content={`https://heffner.dev/og?message=photo%20gallery`}
/>
</svelte:head>

Expand All @@ -32,14 +32,14 @@
</Slice>

{#each items as trip}
<Slice title={trip.title} date={trip.date} warn={trip.warn}>
<a href="/gallery/{trip.slug}" style="display: inline-block" class="not-prose w-full">
<img src={trip.image} alt={trip.alt} class="gallery-image" loading="lazy" />
<Slice title={trip.title} date={trip.date} warn={trip.warn} titleLink={false}>
<a href="/gallery/{trip.slug}" style="display: inline-block" class="not-prose gallery-link w-full">
<img loading="lazy" src={trip.image} alt={trip.alt} class="gallery-image lazy-image" height="100%" width="100%" />
{'>> ' + trip.description + ' <<'}
</a>

{#if trip.title === 'Japan'}
<p class="text-secondary font-bold text-xs">The above link has <strong>>150 MB</strong> worth of images. Please be on wifi.</p>
<p class="text-secondary font-bold text-xs">The above link has <strong>>500 MB</strong> worth of images. Please be on wifi.</p>
{/if}
</Slice>
{/each}
Expand All @@ -52,8 +52,9 @@
<style>
.gallery-image {
max-height: 600px;
width: 100%;
height: 100%;
object-fit: cover;
}
.gallery-link:hover {
background-size: 4px 100px;
}
</style>
Loading

0 comments on commit cdd8dff

Please sign in to comment.