Extract page selection to own component

This commit is contained in:
Johannes Zillmann 2021-02-15 20:08:15 +01:00
parent e6ba5e4812
commit 1f247a8a3b
4 changed files with 84 additions and 49 deletions

View File

@ -0,0 +1,22 @@
<script>
import { writable } from 'svelte/store';
import { setContext } from 'svelte';
import { clickOutside } from '../actions/clickOutside';
let opened = writable(false);
setContext('popupOpened', opened);
function toogle() {
opened.update((old) => !old);
}
</script>
<span on:click|stopPropagation={toogle}>
<slot name="trigger" />
</span>
{#if $opened}
<span use:clickOutside={{ enabled: opened, cb: () => opened.set(false) }}>
<slot name="content" />
</span>
{/if}

View File

@ -1,24 +1,24 @@
<script> <script>
import { slide } from 'svelte/transition'; import { slide } from 'svelte/transition';
import { clickOutside } from '../actions/clickOutside';
import Icon from 'fa-svelte'; import Icon from 'fa-svelte';
import { faMapPin as pin } from '@fortawesome/free-solid-svg-icons/faMapPin'; import { faMapPin as pin } from '@fortawesome/free-solid-svg-icons/faMapPin';
import { Collection, BookOpen, ArrowLeft, ArrowRight } from 'svelte-hero-icons'; import { BookOpen, ArrowLeft, ArrowRight } from 'svelte-hero-icons';
import type Debugger from '@core/Debugger'; import type Debugger from '@core/Debugger';
import type Item from '@core/Item'; import type Item from '@core/Item';
import Popup from '../components/Popup.svelte';
import PageSelectionPopup from './PageSelectionPopup.svelte';
import ItemTable from './ItemTable.svelte'; import ItemTable from './ItemTable.svelte';
export let debug: Debugger; export let debug: Debugger;
const stageNames = debug.stageNames; const stageNames = debug.stageNames;
let openedPageIndex = false; let pinnedPage: number;
let focusedPage: number;
let currentStage = 0; let currentStage = 0;
$: canNext = currentStage + 1 < stageNames.length; $: canNext = currentStage + 1 < stageNames.length;
$: canPrev = currentStage > 0; $: canPrev = currentStage > 0;
$: stageResult = debug.stageResults(currentStage); $: stageResult = debug.stageResults(currentStage);
$: pageFocus = !isNaN(focusedPage); $: pageIsPinned = !isNaN(pinnedPage);
$: pagesNumbers = new Set(stageResult.items.map((item) => item.page)); $: pagesNumbers = new Set(stageResult.items.map((item) => item.page));
$: maxPage = Math.max(...pagesNumbers); $: maxPage = Math.max(...pagesNumbers);
$: itemsByPage = [ $: itemsByPage = [
@ -30,17 +30,7 @@
return map; return map;
}, new Map<number, Item[]>()), }, new Map<number, Item[]>()),
]; ];
$: visiblePages = pageFocus ? itemsByPage.filter(([page]) => page === focusedPage) : itemsByPage; $: visiblePages = pageIsPinned ? itemsByPage.filter(([page]) => page === pinnedPage) : itemsByPage;
function focusOnPage(pageNumber: number) {
openedPageIndex = false;
focusedPage = pageNumber;
}
function showAllPages() {
openedPageIndex = false;
focusedPage = undefined;
}
</script> </script>
<div class="mx-4"> <div class="mx-4">
@ -51,40 +41,25 @@
<!-- Sticky Controls --> <!-- Sticky Controls -->
<div class="controls py-2"> <div class="controls py-2">
<div class="flex items-center space-x-2"> <div class="flex items-center space-x-2">
{#if pageFocus} {#if pageIsPinned}
<span on:click={showAllPages} transition:slide> <span on:click={() => (pinnedPage = undefined)} transition:slide>
<Icon class="text-xs hover:text-green-700 hover:opacity-25 cursor-pointer opacity-75" icon={pin} /> <Icon class="text-xs hover:text-green-700 hover:opacity-25 cursor-pointer opacity-75" icon={pin} />
</span> </span>
{/if} {/if}
<span> <span>
<span on:click|stopPropagation={() => (openedPageIndex = !openedPageIndex)}> <Popup>
<span slot="trigger">
<BookOpen size="1x" class="hover:text-green-700 cursor-pointer" /> <BookOpen size="1x" class="hover:text-green-700 cursor-pointer" />
</span> </span>
<span slot="content">
<!-- Page selection popup--> <PageSelectionPopup
{#if openedPageIndex} {pagesNumbers}
<div {maxPage}
use:clickOutside={{ enabled: openedPageIndex, cb: () => (openedPageIndex = false) }} {pinnedPage}
class="absolute mt-2 p-2 flex bg-gray-200 shadow-lg rounded-sm overflow-auto max-h-96" on:pinPage={(e) => (pinnedPage = e.detail)}
transition:slide> on:unpinPage={() => (pinnedPage = undefined)} />
<span class="mt-1 pr-2" on:click={!!focusedPage && showAllPages}>
<Collection
size="1x"
class={!!focusedPage ? 'hover:text-green-700 cursor-pointer' : 'opacity-50'} />
</span> </span>
<div </Popup>
class="grid gap-3"
style="grid-template-columns: repeat({Math.min(20, maxPage + 1)}, minmax(0, 1fr));">
{#each new Array(maxPage + 1) as _, idx}
<div
on:click={() => pagesNumbers.has(idx) && focusOnPage(idx)}
class="px-2 border border-gray-300 rounded-full text-center {pagesNumbers.has(idx) ? (focusedPage === idx ? 'bg-green-600' : 'hover:text-green-700 hover:border-green-700 cursor-pointer') : 'opacity-50'}">
{idx}
</div>
{/each}
</div>
</div>
{/if}
</span> </span>
<div>|</div> <div>|</div>
@ -109,7 +84,7 @@
</ul> </ul>
<!-- Items --> <!-- Items -->
<ItemTable schema={stageResult.schema} itemsByPage={visiblePages} {maxPage} {pageFocus} /> <ItemTable schema={stageResult.schema} itemsByPage={visiblePages} {maxPage} {pageIsPinned} />
</div> </div>
<style> <style>

View File

@ -7,7 +7,7 @@
export let schema: AnnotatedColumn[]; export let schema: AnnotatedColumn[];
export let itemsByPage: [number, Item[]][]; export let itemsByPage: [number, Item[]][];
export let maxPage: number; export let maxPage: number;
export let pageFocus: boolean; export let pageIsPinned: boolean;
function format(value: object) { function format(value: object) {
if (typeof value === 'number') { if (typeof value === 'number') {
@ -54,7 +54,7 @@
<!-- Page number in first page item row --> <!-- Page number in first page item row -->
{#if itemIdx === 0} {#if itemIdx === 0}
<td class="page bg-gray-50"> <td class="page bg-gray-50">
<div>Page {pageNumber} {pageFocus ? '' : ' / ' + maxPage}</div> <div>Page {pageNumber} {pageIsPinned ? '' : ' / ' + maxPage}</div>
</td> </td>
{:else} {:else}
<td /> <td />

View File

@ -0,0 +1,38 @@
<script>
import { slide } from 'svelte/transition';
import { createEventDispatcher } from 'svelte';
import { getContext } from 'svelte';
import { Collection } from 'svelte-hero-icons';
import type { Writable } from 'svelte/store';
export let pagesNumbers: Set<number>;
export let maxPage: number;
export let pinnedPage: number;
const popupOpened: Writable<boolean> = getContext('popupOpened');
const dispatch = createEventDispatcher();
function pinPage(index: number) {
popupOpened.set(false);
dispatch('pinPage', index);
}
function unpinPage() {
popupOpened.set(false);
dispatch('unpinPage');
}
</script>
<div class="absolute mt-2 p-2 flex bg-gray-200 shadow-lg rounded-sm overflow-auto max-h-96" transition:slide>
<span class="mt-1 pr-2" on:click={!!pinnedPage && unpinPage}>
<Collection size="1x" class={!!pinnedPage ? 'hover:text-green-700 cursor-pointer' : 'opacity-50'} />
</span>
<div class="grid gap-3" style="grid-template-columns: repeat({Math.min(20, maxPage + 1)}, minmax(0, 1fr));">
{#each new Array(maxPage + 1) as _, idx}
<div
on:click={() => pagesNumbers.has(idx) && pinPage(idx)}
class="px-2 border border-gray-300 rounded-full text-center {pagesNumbers.has(idx) ? (pinnedPage === idx ? 'bg-green-600' : 'hover:text-green-700 hover:border-green-700 cursor-pointer') : 'opacity-50'}">
{idx}
</div>
{/each}
</div>
</div>