mirror of
https://github.com/jzillmann/pdf-to-markdown.git
synced 2024-11-30 19:54:23 +01:00
Extract page selection to own component
This commit is contained in:
parent
e6ba5e4812
commit
1f247a8a3b
22
ui/src/components/Popup.svelte
Normal file
22
ui/src/components/Popup.svelte
Normal 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}
|
@ -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>
|
||||||
|
@ -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 />
|
||||||
|
38
ui/src/debug/PageSelectionPopup.svelte
Normal file
38
ui/src/debug/PageSelectionPopup.svelte
Normal 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>
|
Loading…
Reference in New Issue
Block a user