mirror of
https://github.com/jzillmann/pdf-to-markdown.git
synced 2025-06-25 12:01:45 +02:00
Extract ControlBar component
This commit is contained in:
parent
7b833417b5
commit
1154eb3eab
@ -17,8 +17,14 @@
|
|||||||
</span>
|
</span>
|
||||||
|
|
||||||
{#if $opened}
|
{#if $opened}
|
||||||
<span>
|
<span class="popupContent">
|
||||||
<slot name="content" />
|
<slot name="content" class="z-20" />
|
||||||
</span>
|
</span>
|
||||||
{/if}
|
{/if}
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.popupContent :global(*) {
|
||||||
|
@apply z-20;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
125
ui/src/debug/ControlBar.svelte
Normal file
125
ui/src/debug/ControlBar.svelte
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
<script>
|
||||||
|
import { slide } from 'svelte/transition';
|
||||||
|
import slideH from '../svelte/slideH';
|
||||||
|
import { linear } from 'svelte/easing';
|
||||||
|
|
||||||
|
import Icon from 'fa-svelte';
|
||||||
|
import { faMapPin as pin } from '@fortawesome/free-solid-svg-icons/faMapPin';
|
||||||
|
import { BookOpen, ArrowLeft, ArrowRight } from 'svelte-hero-icons';
|
||||||
|
|
||||||
|
import { debugStage } from '../config';
|
||||||
|
import type StageResult from '@core/debug/StageResult';
|
||||||
|
|
||||||
|
import Popup from '../components/Popup.svelte';
|
||||||
|
import PageSelectionPopup from './PageSelectionPopup.svelte';
|
||||||
|
import Checkbox from '../components/Checkbox.svelte';
|
||||||
|
import TransformerSelectionPopup from './TransformerSelectionPopup.svelte';
|
||||||
|
import FontEntry from './FontEntry.svelte';
|
||||||
|
|
||||||
|
export let stageNames: string[];
|
||||||
|
export let stageDescriptions: string[];
|
||||||
|
export let fontMap: Map<string, object>;
|
||||||
|
export let stageResult: StageResult;
|
||||||
|
export let supportsGrouping: boolean;
|
||||||
|
export let supportsRelevanceFiltering: boolean;
|
||||||
|
|
||||||
|
export let pinnedPage: number;
|
||||||
|
export let groupingEnabled = true;
|
||||||
|
export let onlyRelevantItems = true;
|
||||||
|
|
||||||
|
$: canNext = $debugStage + 1 < stageNames.length;
|
||||||
|
$: canPrev = $debugStage > 0;
|
||||||
|
$: pagesNumbers = new Set(stageResult.pages.map((page) => page.index));
|
||||||
|
$: maxPage = Math.max(...pagesNumbers);
|
||||||
|
$: pageIsPinned = !isNaN(pinnedPage);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="controls py-2">
|
||||||
|
<div class="flex items-center space-x-2">
|
||||||
|
{#if pageIsPinned}
|
||||||
|
<span on:click={() => (pinnedPage = undefined)} transition:slideH={{ duration: 180, easing: linear }}>
|
||||||
|
<Icon class="text-xs hover:text-select hover:opacity-25 cursor-pointer opacity-75" icon={pin} />
|
||||||
|
</span>
|
||||||
|
{/if}
|
||||||
|
<span>
|
||||||
|
<Popup>
|
||||||
|
<span slot="trigger" let:opened>
|
||||||
|
<BookOpen size="1x" class="hover:text-select cursor-pointer {opened && 'text-select'}" />
|
||||||
|
</span>
|
||||||
|
<span slot="content">
|
||||||
|
<PageSelectionPopup
|
||||||
|
{pagesNumbers}
|
||||||
|
{maxPage}
|
||||||
|
{pinnedPage}
|
||||||
|
on:pinPage={(e) => (pinnedPage = e.detail)}
|
||||||
|
on:unpinPage={() => (pinnedPage = undefined)} />
|
||||||
|
</span>
|
||||||
|
</Popup>
|
||||||
|
</span>
|
||||||
|
<span>
|
||||||
|
<Popup>
|
||||||
|
<span slot="trigger" let:opened>
|
||||||
|
<div
|
||||||
|
class="hover:text-select cursor-pointer {opened && 'text-select'}"
|
||||||
|
style="font-family: AmericanTypewriter, verdana">
|
||||||
|
F
|
||||||
|
</div>
|
||||||
|
</span>
|
||||||
|
<span slot="content">
|
||||||
|
<div class="absolute mt-1 py-2 px-2 bg-gray-200 rounded-br">
|
||||||
|
<div class=" overflow-y-scroll " style="max-height: 65vh" transition:slide={{ duration: 400 }}>
|
||||||
|
{#each [...fontMap.keys()] as fontName}
|
||||||
|
<FontEntry {fontMap} {fontName} />
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</span>
|
||||||
|
</Popup>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<div>|</div>
|
||||||
|
<div>Transformation:</div>
|
||||||
|
<span on:click={() => canPrev && debugStage.update((cur) => cur - 1)}>
|
||||||
|
<ArrowLeft size="1x" class={canPrev ? 'hover:text-select cursor-pointer' : 'opacity-50'} />
|
||||||
|
</span>
|
||||||
|
<span on:click={() => canNext && debugStage.update((cur) => cur + 1)}>
|
||||||
|
<ArrowRight size="1x" class={canNext ? 'hover:text-select cursor-pointer' : 'opacity-50'} />
|
||||||
|
</span>
|
||||||
|
<span>
|
||||||
|
<Popup>
|
||||||
|
<span slot="trigger">
|
||||||
|
<div class="w-52 cursor-pointer hover:underline whitespace-nowrap">{stageNames[$debugStage]}</div>
|
||||||
|
</span>
|
||||||
|
<span slot="content">
|
||||||
|
<TransformerSelectionPopup
|
||||||
|
{stageNames}
|
||||||
|
{stageDescriptions}
|
||||||
|
currentStage={$debugStage}
|
||||||
|
on:selectTransformer={({ detail }) => debugStage.set(detail)} />
|
||||||
|
</span>
|
||||||
|
</Popup>
|
||||||
|
</span>
|
||||||
|
<div class="w-full flex flex-row-reverse space-x-2 space-x-reverse text-sm">
|
||||||
|
{#if supportsGrouping}
|
||||||
|
<span class="inline-flex" transition:slideH={{ duration: 700 }}>
|
||||||
|
<Checkbox name="Grouped" bind:enabled={groupingEnabled} />
|
||||||
|
</span>
|
||||||
|
{/if}
|
||||||
|
{#if supportsRelevanceFiltering}
|
||||||
|
<span class="inline-flex" transition:slideH={{ duration: 700 }}>
|
||||||
|
<Checkbox name="Relevant Items" bind:enabled={onlyRelevantItems} />
|
||||||
|
</span>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.controls {
|
||||||
|
@apply bg-gray-50;
|
||||||
|
position: -webkit-sticky;
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
z-index: 3;
|
||||||
|
}
|
||||||
|
</style>
|
@ -1,36 +1,23 @@
|
|||||||
<script>
|
<script>
|
||||||
import { slide } from 'svelte/transition';
|
import { slide } from 'svelte/transition';
|
||||||
import { linear } from 'svelte/easing';
|
|
||||||
import Icon from 'fa-svelte';
|
|
||||||
import { faMapPin as pin } from '@fortawesome/free-solid-svg-icons/faMapPin';
|
|
||||||
import { BookOpen, ArrowLeft, ArrowRight } from 'svelte-hero-icons';
|
|
||||||
|
|
||||||
import type Debugger from '@core/Debugger';
|
import type Debugger from '@core/Debugger';
|
||||||
|
|
||||||
import slideH from '../svelte/slideH';
|
|
||||||
import Popup from '../components/Popup.svelte';
|
|
||||||
import PageSelectionPopup from './PageSelectionPopup.svelte';
|
|
||||||
import Checkbox from '../components/Checkbox.svelte';
|
|
||||||
import ItemTable from './ItemTable.svelte';
|
|
||||||
import TransformerSelectionPopup from './TransformerSelectionPopup.svelte';
|
|
||||||
import { debugStage } from '../config';
|
import { debugStage } from '../config';
|
||||||
import FontEntry from './FontEntry.svelte';
|
import ControlBar from './ControlBar.svelte';
|
||||||
|
import ItemTable from './ItemTable.svelte';
|
||||||
|
|
||||||
export let debug: Debugger;
|
export let debug: Debugger;
|
||||||
|
|
||||||
const stageNames = debug.stageNames;
|
const stageNames = debug.stageNames;
|
||||||
let pinnedPage: number;
|
let pinnedPage: number;
|
||||||
let onlyRelevantItems = true;
|
|
||||||
let groupingEnabled = true;
|
let groupingEnabled = true;
|
||||||
|
let onlyRelevantItems = true;
|
||||||
|
|
||||||
$: canNext = $debugStage + 1 < stageNames.length;
|
|
||||||
$: canPrev = $debugStage > 0;
|
|
||||||
$: stageResult = debug.stageResults($debugStage);
|
$: stageResult = debug.stageResults($debugStage);
|
||||||
$: supportsGrouping = !!stageResult.descriptor?.debug?.itemMerger;
|
$: supportsGrouping = !!stageResult.descriptor?.debug?.itemMerger;
|
||||||
$: supportsRelevanceFiltering = !stageResult.descriptor?.debug?.showAll;
|
$: supportsRelevanceFiltering = !stageResult.descriptor?.debug?.showAll;
|
||||||
$: pageIsPinned = !isNaN(pinnedPage);
|
$: pageIsPinned = !isNaN(pinnedPage);
|
||||||
$: pagesNumbers = new Set(stageResult.pages.map((page) => page.index));
|
|
||||||
$: maxPage = Math.max(...pagesNumbers);
|
|
||||||
$: visiblePages = stageResult.selectPages(onlyRelevantItems, groupingEnabled, pinnedPage);
|
$: visiblePages = stageResult.selectPages(onlyRelevantItems, groupingEnabled, pinnedPage);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@ -40,90 +27,16 @@
|
|||||||
<div>Author: {parseResult.metadata.author()}</div> -->
|
<div>Author: {parseResult.metadata.author()}</div> -->
|
||||||
|
|
||||||
<!-- Sticky Controls -->
|
<!-- Sticky Controls -->
|
||||||
<div class="controls py-2">
|
<ControlBar
|
||||||
<div class="flex items-center space-x-2">
|
{stageNames}
|
||||||
{#if pageIsPinned}
|
stageDescriptions={debug.stageDescriptions}
|
||||||
<span on:click={() => (pinnedPage = undefined)} transition:slideH={{ duration: 180, easing: linear }}>
|
fontMap={debug.fontMap}
|
||||||
<Icon class="text-xs hover:text-select hover:opacity-25 cursor-pointer opacity-75" icon={pin} />
|
{stageResult}
|
||||||
</span>
|
{supportsGrouping}
|
||||||
{/if}
|
{supportsRelevanceFiltering}
|
||||||
<span>
|
bind:groupingEnabled
|
||||||
<Popup>
|
bind:onlyRelevantItems
|
||||||
<span slot="trigger" let:opened>
|
bind:pinnedPage />
|
||||||
<BookOpen size="1x" class="hover:text-select cursor-pointer {opened && 'text-select'}" />
|
|
||||||
</span>
|
|
||||||
<span slot="content">
|
|
||||||
<PageSelectionPopup
|
|
||||||
{pagesNumbers}
|
|
||||||
{maxPage}
|
|
||||||
{pinnedPage}
|
|
||||||
on:pinPage={(e) => (pinnedPage = e.detail)}
|
|
||||||
on:unpinPage={() => (pinnedPage = undefined)} />
|
|
||||||
</span>
|
|
||||||
</Popup>
|
|
||||||
</span>
|
|
||||||
<span>
|
|
||||||
<Popup>
|
|
||||||
<span slot="trigger" let:opened>
|
|
||||||
<div
|
|
||||||
class="hover:text-select cursor-pointer {opened && 'text-select'}"
|
|
||||||
style="font-family: AmericanTypewriter, verdana">
|
|
||||||
F
|
|
||||||
</div>
|
|
||||||
</span>
|
|
||||||
<span slot="content">
|
|
||||||
<div class="absolute mt-1 py-2 px-2 bg-gray-200 rounded-br">
|
|
||||||
<div
|
|
||||||
class=" overflow-y-scroll "
|
|
||||||
style="max-height: 65vh"
|
|
||||||
transition:slide={{ duration: 400 }}>
|
|
||||||
{#each [...debug.fontMap.keys()] as fontName}
|
|
||||||
<FontEntry fontMap={debug.fontMap} {fontName} />
|
|
||||||
{/each}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</span>
|
|
||||||
</Popup>
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<div>|</div>
|
|
||||||
<div>Transformation:</div>
|
|
||||||
<span on:click={() => canPrev && debugStage.update((cur) => cur - 1)}>
|
|
||||||
<ArrowLeft size="1x" class={canPrev ? 'hover:text-select cursor-pointer' : 'opacity-50'} />
|
|
||||||
</span>
|
|
||||||
<span on:click={() => canNext && debugStage.update((cur) => cur + 1)}>
|
|
||||||
<ArrowRight size="1x" class={canNext ? 'hover:text-select cursor-pointer' : 'opacity-50'} />
|
|
||||||
</span>
|
|
||||||
<span>
|
|
||||||
<Popup>
|
|
||||||
<span slot="trigger">
|
|
||||||
<div class="w-52 cursor-pointer hover:underline whitespace-nowrap">
|
|
||||||
{stageNames[$debugStage]}
|
|
||||||
</div>
|
|
||||||
</span>
|
|
||||||
<span slot="content">
|
|
||||||
<TransformerSelectionPopup
|
|
||||||
{stageNames}
|
|
||||||
stageDescriptions={debug.stageDescriptions}
|
|
||||||
currentStage={$debugStage}
|
|
||||||
on:selectTransformer={({ detail }) => debugStage.set(detail)} />
|
|
||||||
</span>
|
|
||||||
</Popup>
|
|
||||||
</span>
|
|
||||||
<div class="w-full flex flex-row-reverse space-x-2 space-x-reverse text-sm">
|
|
||||||
{#if supportsGrouping}
|
|
||||||
<span class="inline-flex" transition:slideH={{ duration: 700 }}>
|
|
||||||
<Checkbox name="Grouped" bind:enabled={groupingEnabled} />
|
|
||||||
</span>
|
|
||||||
{/if}
|
|
||||||
{#if supportsRelevanceFiltering}
|
|
||||||
<span class="inline-flex" transition:slideH={{ duration: 700 }}>
|
|
||||||
<Checkbox name="Relevant Items" bind:enabled={onlyRelevantItems} />
|
|
||||||
</span>
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Stage Messages -->
|
<!-- Stage Messages -->
|
||||||
<ul
|
<ul
|
||||||
@ -136,12 +49,7 @@
|
|||||||
|
|
||||||
<!-- Items -->
|
<!-- Items -->
|
||||||
{#if visiblePages.find((page) => page.itemGroups.length > 0)}
|
{#if visiblePages.find((page) => page.itemGroups.length > 0)}
|
||||||
<ItemTable
|
<ItemTable schema={stageResult.schema} pages={visiblePages} {pageIsPinned} changes={stageResult.changes} />
|
||||||
schema={stageResult.schema}
|
|
||||||
pages={visiblePages}
|
|
||||||
{maxPage}
|
|
||||||
{pageIsPinned}
|
|
||||||
changes={stageResult.changes} />
|
|
||||||
{:else}
|
{:else}
|
||||||
<!-- No items visible -->
|
<!-- No items visible -->
|
||||||
<div class="flex space-x-1 items-center justify-center text-xl">
|
<div class="flex space-x-1 items-center justify-center text-xl">
|
||||||
@ -165,13 +73,6 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.controls {
|
|
||||||
@apply bg-gray-50;
|
|
||||||
position: -webkit-sticky;
|
|
||||||
position: sticky;
|
|
||||||
top: 0;
|
|
||||||
z-index: 3;
|
|
||||||
}
|
|
||||||
.messages {
|
.messages {
|
||||||
transition: max-height 0.15s ease-in-out;
|
transition: max-height 0.15s ease-in-out;
|
||||||
}
|
}
|
||||||
|
@ -10,9 +10,10 @@
|
|||||||
|
|
||||||
export let schema: AnnotatedColumn[];
|
export let schema: AnnotatedColumn[];
|
||||||
export let pages: Page[];
|
export let pages: Page[];
|
||||||
export let maxPage: number;
|
|
||||||
export let pageIsPinned: boolean;
|
export let pageIsPinned: boolean;
|
||||||
export let changes: ChangeIndex;
|
export let changes: ChangeIndex;
|
||||||
|
|
||||||
|
let maxPage = pages[pages.length - 1].index;
|
||||||
let maxItemsToRenderInOneLoad = 200;
|
let maxItemsToRenderInOneLoad = 200;
|
||||||
let renderedMaxPage = 0;
|
let renderedMaxPage = 0;
|
||||||
let expandedItemGroup: { pageIndex: number; itemIndex: number };
|
let expandedItemGroup: { pageIndex: number; itemIndex: number };
|
||||||
|
Loading…
x
Reference in New Issue
Block a user