upend/webui/src/lib/components/display/blobs/TextViewer.svelte

114 lines
2.2 KiB
Svelte

<script lang="ts">
import api from '$lib/api';
import IconButton from '../../utils/IconButton.svelte';
import Spinner from '../../utils/Spinner.svelte';
export let address: string;
let mode: 'preview' | 'full' | 'markdown' = 'preview';
$: textContent = (async () => {
const response = await api.fetchRaw(address, mode == 'preview');
const text = await response.text();
if (mode === 'markdown') {
const { marked } = await import('marked');
const DOMPurify = await import('dompurify');
return DOMPurify.default.sanitize(marked.parse(text));
} else {
return text;
}
})();
const tabs = [
['image', 'preview', 'Preview'],
['shape-circle', 'full', 'Full'],
['edit', 'markdown', 'Markdown']
] as [string, typeof mode, string][];
</script>
<div class="text-preview">
<header class="text-header">
{#each tabs as [icon, targetMode, label]}
<div
class="tab"
class:active={mode == targetMode}
on:click={() => (mode = targetMode)}
on:keydown={(ev) => {
if (ev.key === 'Enter') {
mode = targetMode;
}
}}
>
<IconButton name={icon} active={mode == targetMode} on:click={() => (mode = targetMode)} />
<div class="label">{label}</div>
</div>
{/each}
</header>
<div class="text" class:markdown={mode === 'markdown'}>
{#await textContent}
<Spinner centered />
{:then text}
{#if mode === 'markdown'}
{@html text}
{:else}
{text}{#if mode === 'preview'}{/if}
{/if}
{/await}
</div>
</div>
<style lang="scss">
.text-preview {
flex: 1;
min-width: 0;
}
.text {
background: var(--background);
padding: 0.5em;
height: 100%;
box-sizing: border-box;
overflow: auto;
border-radius: 4px;
border: 1px solid var(--foreground);
white-space: pre-wrap;
&.markdown {
white-space: unset;
:global(img) {
max-width: 75%;
}
}
}
header {
display: flex;
justify-content: flex-end;
.tab {
display: flex;
cursor: pointer;
border: 1px solid var(--foreground);
border-bottom: 0;
border-top-left-radius: 4px;
border-top-right-radius: 4px;
padding: 0.15em;
margin: 0 0.1em;
&.active {
background: var(--background);
}
.label {
margin-right: 0.5em;
}
}
}
</style>