118 lines
2.5 KiB
Svelte
118 lines
2.5 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>
|