Skip to content

Commit

Permalink
Textzeugen overhaul
Browse files Browse the repository at this point in the history
  • Loading branch information
Sebastian Flick committed Oct 23, 2024
1 parent 10e2597 commit b952171
Show file tree
Hide file tree
Showing 3 changed files with 113 additions and 89 deletions.
11 changes: 4 additions & 7 deletions src/lib/components/TextzeugenSelector.svelte
Original file line number Diff line number Diff line change
@@ -1,19 +1,16 @@
<script>
import VerseSelector from '$lib/components/VerseSelector.svelte';
/** @type {{sigla?: {sigil: String, loc: String, aka: String, cod: String, handle: String}[], selectedSigla?: any, coordinates?: [String | boolean, String | boolean]}} */
let { sigla = [], selectedSigla = $bindable(['d']), coordinates = ['', ''] } = $props();
let { sigla = [], selectedSigla = ['d'], coordinates = ['', ''] } = $props();
let selection = $state(selectedSigla);
</script>

<div>
<div class="flex gap-1 my-3">
{#each Array.from({ length: 2 }) as _, i}
<label>
Textzeuge: <select class="select my-2" bind:value={selectedSigla[i]}>
Textzeuge: <select class="select my-2" bind:value={selection[i]}>
{#if i !== 0}<option value="">kein Textzeuge</option>{/if}
{#each sigla as { sigil, handle }}
<option value={handle}>{@html sigil}</option>
Expand All @@ -24,7 +21,7 @@
</div>
<div class="flex max-w-full items-baseline gap-1 my-3">
<VerseSelector
targetPath={`/textzeugen/${selectedSigla.filter((e) => !!e).join('-')}`}
targetPath={`/textzeugen/${selection.filter((e) => !!e).join('-')}`}
{coordinates}
/>
</div>
Expand Down
110 changes: 64 additions & 46 deletions src/routes/textzeugen/[[sigla]]/[[thirties]]/[[verse]]/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import TextzeugenContent, { setTarget } from './TextzeugenContent.svelte';
import { base } from '$app/paths';
import { page } from '$app/stores';
import { afterNavigate, replaceState } from '$app/navigation';
import { replaceState } from '$app/navigation';
import { iiif } from '$lib/constants';
/** @type {{data: import('./$types').PageData}} */
Expand Down Expand Up @@ -60,38 +60,41 @@
}
return link.toString();
};
setTarget(`${data.thirties}.${data.verse}`);
let localVerses = $state(Array(data.content?.length).fill(`${data.thirties}.${data.verse}`));
let localPages = $state(Array(data.content?.length).fill([]));
let currentIiif = $state(Array(data.content?.length).fill({}));
afterNavigate(() => {
console.log('navigated');
//fill the data from the load-function into the localPages array
data.content?.forEach((c, i) => {
const generateLocalPagesFromData = (d) => {
return d?.map((c) => {
if (typeof c.meta === 'object') {
c.meta.then((meta) => {
localPages[i] = [...meta];
meta
// @ts-ignore
.find((m) => m.active)
// @ts-ignore
?.iiif.then((iiif) => {
console.log('setting iiif', iiif);
currentIiif[i] = iiif;
});
return c.meta.then((meta) => {
return meta;
});
}
return [];
});
};
let localPages = $state(generateLocalPagesFromData(data.content));
const generateIiifFromData = (d) => {
return d?.map(async (c) => {
if (typeof c.meta === 'object') {
let meta = await c.meta;
let active = meta.find((m) => m.active);
return await active.iiif;
}
});
};
let currentIiif = $state(generateIiifFromData(data.content));
$effect(() => {
localPages = generateLocalPagesFromData(data.content);
currentIiif = generateIiifFromData(data.content);
});
const checklocalPages = (
const checklocalPages = async (
/** @type {CustomEvent<any>} */ e,
/** @type {number} */ i,
/** @type {string} */ sigla
) => {
const indexCurrent = localPages[i].findIndex(
const indexCurrent = (await localPages[i]).findIndex(
(/** @type {{ id: string; }} */ p) => p.id === e.detail.id
);
// Don't switch the iiif viewer on page change, just on click
Expand All @@ -113,14 +116,12 @@
break;
case 0:
if (e.detail.previous) {
console.log('fetching previous', e.detail.previous);
localPages[i] = [createObject(e.detail.previous), ...localPages[i]];
localPages[i] = [createObject(e.detail.previous), ...(await localPages[i])];
}
break;
case localPages[i].length - 1:
if (e.detail.next) {
console.log('fetching next', e.detail.next);
localPages[i] = [...localPages[i], createObject(e.detail.next)];
localPages[i] = [...(await localPages[i]), createObject(e.detail.next)];
}
break;
}
Expand Down Expand Up @@ -156,40 +157,57 @@
</p>
<div class="absolute top-0 right-0">
{#if !($page.url.searchParams.get('iiif')?.split('-')[i] === 'false')}
<a class="btn btn-icon" href={generateIiifLink($page.url, i, false)}>
<a
class="btn btn-icon"
href={generateIiifLink($page.url, i, false)}
aria-label="Faksimile verstecken"
>
<i class="fa-solid fa-eye-slash"></i>
</a>
{:else}
<a class="btn btn-icon" href={generateIiifLink($page.url, i, true)}>
<a
class="btn btn-icon"
href={generateIiifLink($page.url, i, true)}
aria-label="Faksimile anzeigen"
>
<i class="fa-solid fa-eye"></i>
</a>
{/if}
<a class="btn btn-icon" href={generateCloseLink(content.sigla)}>
<a
class="btn btn-icon"
href={generateCloseLink(content.sigla)}
aria-label="Ansicht schliessen"
>
<i class="fa-solid fa-x"></i>
</a>
</div>
</div>
<TextzeugenContent
pages={localPages[i]}
on:localVerseChange={(e) => {
localVerses[i] = e.detail;
replaceState(
`${base}/textzeugen/${$page.params.sigla}/${e.detail.replace('.', '/')}?${$page.url.searchParams.toString()}`,
{}
);
}}
on:localPageChange={(e) => checklocalPages(e, i, content.sigla)}
on:localIiifChange={(e) => {
console.log('setting iiif');
currentIiif[i] = e.detail;
}}
/>
{#await localPages[i]}
Lade Text...
{:then pages}
<TextzeugenContent
{pages}
on:localVerseChange={(e) => {
localVerses[i] = e.detail;
replaceState(
`${base}/textzeugen/${$page.params.sigla}/${e.detail.replace('.', '/')}?${$page.url.searchParams.toString()}`,
{}
);
}}
on:localPageChange={(e) => checklocalPages(e, i, content.sigla)}
on:localIiifChange={(e) => {
currentIiif[i] = e.detail;
}}
/>
{/await}
</section>
{#if !($page.url.searchParams.get('iiif')?.split('-')[i] === 'false')}
<section class="min-h-[40vh]">
{#if typeof currentIiif[i] === 'object' && Object.keys(currentIiif[i]).length}
<IIIFViewer iiif={currentIiif[i]} />
{/if}
{#await currentIiif[i] then current}
{#if typeof current === 'object' && Object.keys(current).length}
<IIIFViewer iiif={current} />
{/if}
{/await}
</section>
{/if}
</article>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,22 @@

<script>
import { createEventDispatcher, onMount } from 'svelte';
import { page } from '$app/stores';
/** @type {{pages: any}} */
let { pages } = $props();
let localVerse = $targetVerse;
let localVerse = 0;
/**
* @type {number | undefined}
*/
let timer;
const dispatch = createEventDispatcher();
let scrollContainer = $state(), observer;
let scrollContainer = $state();
/**
* @type {IntersectionObserver}
*/
let observer;
onMount(() => {
observer = new IntersectionObserver(
Expand All @@ -42,15 +45,16 @@
});
let programmaticScroll = false;
let oldHeight = 0;
const onScrollEnd = (/** @type { Event & { target: HTMLElement}} } */ e) => {
if (programmaticScroll) {
if (programmaticScroll || scrollContainer.scrollHeight > oldHeight) {
programmaticScroll = false;
oldHeight = scrollContainer.scrollHeight;
} else {
clearTimeout(timer);
timer = setTimeout(() => {
const positive = (/** @type {string} */ verse) => {
localVerse = verse;
$targetVerse = verse;
dispatch('localVerseChange', verse);
};
Expand Down Expand Up @@ -84,36 +88,41 @@
}
};
const scrollToVerse = (/** @type {HTMLDivElement} */ node, /** @type {String} */ targetVerse) => {
const scroll = (/** @type {String} */ target) => {
const verse = node.parentElement?.querySelector(`[data-verse="${target}"]`);
if (!verse) return;
programmaticScroll = true;
verse.scrollIntoView({ behavior: 'instant', block: 'start' });
// scrollContainer?.scrollTo({
// top:
// scrollContainer?.scrollTop +
// Number(verse.parentElement?.getBoundingClientRect().top) -
// scrollContainer?.getBoundingClientRect().top,
// behavior: 'instant'
// });
dispatch('localVerseChange', target);
verse.parentElement?.classList.add('animate-pulse', 'once');
};
scroll(targetVerse);
observer.observe(node);
return {
/**
* @param {String} targetVerse
*/
update(targetVerse) {
if (targetVerse === localVerse) return;
scroll(targetVerse);
},
destroy() {}
};
const scroll = async (/** @type {String} */ target) => {
localVerse = Number(target);
programmaticScroll = true;
//wait for promises in pages to resolve before scrolling
await Promise.all(
pages.map((/** @type {{ tpData: any; }} */ page) => {
return page.tpData;
})
);
const verse = scrollContainer.querySelector(`[data-verse="${target}"]`);
if (!verse) return;
// verse.scrollIntoView({ behavior: 'instant', block: 'start' });
scrollContainer?.scrollTo({
top:
scrollContainer?.scrollTop +
Number(verse.parentElement?.getBoundingClientRect().top) -
scrollContainer?.getBoundingClientRect().top,
behavior: 'instant'
});
dispatch('localVerseChange', target);
verse.parentElement?.classList.add('animate-pulse', 'once');
programmaticScroll = false;
};
$effect(() => {
if (!programmaticScroll && localVerse !== Number($targetVerse)) {
scroll($targetVerse);
}
});
const addToObserver = (/** @type {HTMLDivElement} */ node) => {
$effect(() => {
observer.observe(node);
return () => {
observer.unobserve(node);
};
});
};
// returns true when the column is empty or when it contains only children that are empty or themselves have empty children (recursively)
const isEmptyColumn = (/** @type {String} */ column) => {
Expand Down Expand Up @@ -151,7 +160,7 @@
data-id={pageObject.id}
data-next={tpData.nextId}
data-previous={tpData.previousId}
use:scrollToVerse={$targetVerse}
use:addToObserver
>
{#await pageObject.iiif then iiif}
<button
Expand Down

0 comments on commit b952171

Please sign in to comment.