mirror of
https://github.com/seanmorley15/AdventureLog.git
synced 2025-07-28 17:29:36 +02:00
Add Markdown editor component and integrate it into AdventureModal
This commit is contained in:
parent
7d609d01ea
commit
dd08a6fe24
14 changed files with 131 additions and 20 deletions
|
@ -41,6 +41,7 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@lukulent/svelte-umami": "^0.0.3",
|
"@lukulent/svelte-umami": "^0.0.3",
|
||||||
"emoji-picker-element": "^1.26.0",
|
"emoji-picker-element": "^1.26.0",
|
||||||
|
"marked": "^15.0.4",
|
||||||
"qrcode": "^1.5.4",
|
"qrcode": "^1.5.4",
|
||||||
"svelte-i18n": "^4.0.1",
|
"svelte-i18n": "^4.0.1",
|
||||||
"svelte-maplibre": "^0.9.8"
|
"svelte-maplibre": "^0.9.8"
|
||||||
|
|
10
frontend/pnpm-lock.yaml
generated
10
frontend/pnpm-lock.yaml
generated
|
@ -14,6 +14,9 @@ importers:
|
||||||
emoji-picker-element:
|
emoji-picker-element:
|
||||||
specifier: ^1.26.0
|
specifier: ^1.26.0
|
||||||
version: 1.26.0
|
version: 1.26.0
|
||||||
|
marked:
|
||||||
|
specifier: ^15.0.4
|
||||||
|
version: 15.0.4
|
||||||
qrcode:
|
qrcode:
|
||||||
specifier: ^1.5.4
|
specifier: ^1.5.4
|
||||||
version: 1.5.4
|
version: 1.5.4
|
||||||
|
@ -1351,6 +1354,11 @@ packages:
|
||||||
resolution: {integrity: sha512-qOS1hn4d/pn2i0uva4S5Oz+fACzTkgBKq+NpwT/Tqzi4MSyzcWNtDELzLUSgWqHfNIkGCl5CZ/w7dtis+t4RCw==}
|
resolution: {integrity: sha512-qOS1hn4d/pn2i0uva4S5Oz+fACzTkgBKq+NpwT/Tqzi4MSyzcWNtDELzLUSgWqHfNIkGCl5CZ/w7dtis+t4RCw==}
|
||||||
engines: {node: '>=16.14.0', npm: '>=8.1.0'}
|
engines: {node: '>=16.14.0', npm: '>=8.1.0'}
|
||||||
|
|
||||||
|
marked@15.0.4:
|
||||||
|
resolution: {integrity: sha512-TCHvDqmb3ZJ4PWG7VEGVgtefA5/euFmsIhxtD0XsBxI39gUSKL81mIRFdt0AiNQozUahd4ke98ZdirExd/vSEw==}
|
||||||
|
engines: {node: '>= 18'}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
mdn-data@2.0.30:
|
mdn-data@2.0.30:
|
||||||
resolution: {integrity: sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==}
|
resolution: {integrity: sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==}
|
||||||
|
|
||||||
|
@ -3318,6 +3326,8 @@ snapshots:
|
||||||
tinyqueue: 2.0.3
|
tinyqueue: 2.0.3
|
||||||
vt-pbf: 3.1.3
|
vt-pbf: 3.1.3
|
||||||
|
|
||||||
|
marked@15.0.4: {}
|
||||||
|
|
||||||
mdn-data@2.0.30: {}
|
mdn-data@2.0.30: {}
|
||||||
|
|
||||||
memoizee@0.4.17:
|
memoizee@0.4.17:
|
||||||
|
|
|
@ -32,6 +32,7 @@
|
||||||
import { appVersion } from '$lib/config';
|
import { appVersion } from '$lib/config';
|
||||||
import CategoryDropdown from './CategoryDropdown.svelte';
|
import CategoryDropdown from './CategoryDropdown.svelte';
|
||||||
import { findFirstValue } from '$lib';
|
import { findFirstValue } from '$lib';
|
||||||
|
import MarkdownEditor from './MarkdownEditor.svelte';
|
||||||
|
|
||||||
let wikiError: string = '';
|
let wikiError: string = '';
|
||||||
|
|
||||||
|
@ -577,15 +578,10 @@
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label for="description">{$t('adventures.description')}</label><br />
|
<label for="description">{$t('adventures.description')}</label><br />
|
||||||
<textarea
|
<MarkdownEditor bind:text={adventure.description} />
|
||||||
id="description"
|
|
||||||
name="description"
|
|
||||||
bind:value={adventure.description}
|
|
||||||
class="textarea textarea-bordered w-full h-32"
|
|
||||||
></textarea>
|
|
||||||
<div class="mt-2">
|
<div class="mt-2">
|
||||||
<div class="tooltip tooltip-right" data-tip={$t('adventures.wiki_desc')}>
|
<div class="tooltip tooltip-right" data-tip={$t('adventures.wiki_desc')}>
|
||||||
<button type="button" class="btn btn-neutral" on:click={generateDesc}
|
<button type="button" class="btn btn-neutral mt-2" on:click={generateDesc}
|
||||||
>{$t('adventures.generate_desc')}</button
|
>{$t('adventures.generate_desc')}</button
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
|
|
80
frontend/src/lib/components/MarkdownEditor.svelte
Normal file
80
frontend/src/lib/components/MarkdownEditor.svelte
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { marked } from 'marked'; // Import the markdown parser
|
||||||
|
import { t } from 'svelte-i18n';
|
||||||
|
|
||||||
|
export let text: string | null | undefined = ''; // Markdown text
|
||||||
|
let is_preview: boolean = false; // Toggle between Edit and Preview mode
|
||||||
|
|
||||||
|
// Function to parse markdown to HTML
|
||||||
|
const renderMarkdown = (markdown: string) => {
|
||||||
|
return marked(markdown);
|
||||||
|
};
|
||||||
|
|
||||||
|
// References for scroll syncing
|
||||||
|
let editorRef: HTMLTextAreaElement | null = null;
|
||||||
|
let previewRef: HTMLElement | null = null;
|
||||||
|
|
||||||
|
// Sync scrolling between editor and preview
|
||||||
|
const syncScroll = () => {
|
||||||
|
if (editorRef && previewRef) {
|
||||||
|
const ratio = editorRef.scrollTop / (editorRef.scrollHeight - editorRef.clientHeight);
|
||||||
|
previewRef.scrollTop = ratio * (previewRef.scrollHeight - previewRef.clientHeight);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="join justify-center mt-2">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="join-item btn btn-sm btn-outline"
|
||||||
|
on:click={() => (is_preview = false)}
|
||||||
|
class:btn-active={!is_preview}
|
||||||
|
>
|
||||||
|
{$t('transportation.edit')}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="join-item btn btn-sm btn-outline"
|
||||||
|
on:click={() => (is_preview = true)}
|
||||||
|
class:btn-active={is_preview}
|
||||||
|
>
|
||||||
|
{$t('adventures.preview')}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex flex-col mt-4 gap-4">
|
||||||
|
<!-- Markdown Editor -->
|
||||||
|
{#if !is_preview}
|
||||||
|
<textarea
|
||||||
|
class="textarea textarea-bordered resize-none h-64 w-full"
|
||||||
|
bind:this={editorRef}
|
||||||
|
bind:value={text}
|
||||||
|
placeholder={$t('adventures.md_instructions')}
|
||||||
|
on:scroll={syncScroll}
|
||||||
|
></textarea>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<!-- Markdown Preview -->
|
||||||
|
{#if is_preview}
|
||||||
|
<article
|
||||||
|
class="prose overflow-auto h-96 max-w-full w-full p-4 border border-base-300 rounded-lg bg-base-300"
|
||||||
|
bind:this={previewRef}
|
||||||
|
>
|
||||||
|
{@html renderMarkdown(text || '')}
|
||||||
|
</article>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
/* Optional: Smooth scrolling for synced scroll effect */
|
||||||
|
textarea,
|
||||||
|
article {
|
||||||
|
scroll-behavior: smooth;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Force both editor and preview to have equal width */
|
||||||
|
textarea,
|
||||||
|
article {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -195,7 +195,9 @@
|
||||||
"emoji_picker": "Emoji-Picker",
|
"emoji_picker": "Emoji-Picker",
|
||||||
"hide": "Verstecken",
|
"hide": "Verstecken",
|
||||||
"show": "Zeigen",
|
"show": "Zeigen",
|
||||||
"download_calendar": "Kalender herunterladen"
|
"download_calendar": "Kalender herunterladen",
|
||||||
|
"md_instructions": "Schreiben Sie hier Ihren Abschlag...",
|
||||||
|
"preview": "Vorschau"
|
||||||
},
|
},
|
||||||
"home": {
|
"home": {
|
||||||
"desc_1": "Entdecken, planen und erkunden Sie mit Leichtigkeit",
|
"desc_1": "Entdecken, planen und erkunden Sie mit Leichtigkeit",
|
||||||
|
|
|
@ -218,6 +218,8 @@
|
||||||
"hide": "Hide",
|
"hide": "Hide",
|
||||||
"emoji_picker": "Emoji Picker",
|
"emoji_picker": "Emoji Picker",
|
||||||
"download_calendar": "Download Calendar",
|
"download_calendar": "Download Calendar",
|
||||||
|
"preview": "Preview",
|
||||||
|
"md_instructions": "Write your markdown here...",
|
||||||
"days": "days",
|
"days": "days",
|
||||||
"activities": {
|
"activities": {
|
||||||
"general": "General 🌍",
|
"general": "General 🌍",
|
||||||
|
|
|
@ -242,7 +242,9 @@
|
||||||
"emoji_picker": "Selector de emojis",
|
"emoji_picker": "Selector de emojis",
|
||||||
"hide": "Esconder",
|
"hide": "Esconder",
|
||||||
"show": "Espectáculo",
|
"show": "Espectáculo",
|
||||||
"download_calendar": "Descargar Calendario"
|
"download_calendar": "Descargar Calendario",
|
||||||
|
"md_instructions": "Escriba su descuento aquí...",
|
||||||
|
"preview": "Avance"
|
||||||
},
|
},
|
||||||
"worldtravel": {
|
"worldtravel": {
|
||||||
"all": "Todo",
|
"all": "Todo",
|
||||||
|
|
|
@ -195,7 +195,9 @@
|
||||||
"emoji_picker": "Sélecteur d'émoticônes",
|
"emoji_picker": "Sélecteur d'émoticônes",
|
||||||
"hide": "Cacher",
|
"hide": "Cacher",
|
||||||
"show": "Montrer",
|
"show": "Montrer",
|
||||||
"download_calendar": "Télécharger le calendrier"
|
"download_calendar": "Télécharger le calendrier",
|
||||||
|
"md_instructions": "Écrivez votre démarque ici...",
|
||||||
|
"preview": "Aperçu"
|
||||||
},
|
},
|
||||||
"home": {
|
"home": {
|
||||||
"desc_1": "Découvrez, planifiez et explorez en toute simplicité",
|
"desc_1": "Découvrez, planifiez et explorez en toute simplicité",
|
||||||
|
|
|
@ -195,7 +195,9 @@
|
||||||
"emoji_picker": "Selettore di emoji",
|
"emoji_picker": "Selettore di emoji",
|
||||||
"hide": "Nascondere",
|
"hide": "Nascondere",
|
||||||
"show": "Spettacolo",
|
"show": "Spettacolo",
|
||||||
"download_calendar": "Scarica Calendario"
|
"download_calendar": "Scarica Calendario",
|
||||||
|
"md_instructions": "Scrivi qui il tuo ribasso...",
|
||||||
|
"preview": "Anteprima"
|
||||||
},
|
},
|
||||||
"home": {
|
"home": {
|
||||||
"desc_1": "Scopri, pianifica ed esplora con facilità",
|
"desc_1": "Scopri, pianifica ed esplora con facilità",
|
||||||
|
|
|
@ -195,7 +195,9 @@
|
||||||
"emoji_picker": "Emoji-kiezer",
|
"emoji_picker": "Emoji-kiezer",
|
||||||
"hide": "Verbergen",
|
"hide": "Verbergen",
|
||||||
"show": "Show",
|
"show": "Show",
|
||||||
"download_calendar": "Agenda downloaden"
|
"download_calendar": "Agenda downloaden",
|
||||||
|
"md_instructions": "Schrijf hier uw korting...",
|
||||||
|
"preview": "Voorbeeld"
|
||||||
},
|
},
|
||||||
"home": {
|
"home": {
|
||||||
"desc_1": "Ontdek, plan en verken met gemak",
|
"desc_1": "Ontdek, plan en verken met gemak",
|
||||||
|
|
|
@ -242,7 +242,9 @@
|
||||||
"emoji_picker": "Wybór emoji",
|
"emoji_picker": "Wybór emoji",
|
||||||
"hide": "Ukrywać",
|
"hide": "Ukrywać",
|
||||||
"show": "Pokazywać",
|
"show": "Pokazywać",
|
||||||
"download_calendar": "Pobierz Kalendarz"
|
"download_calendar": "Pobierz Kalendarz",
|
||||||
|
"md_instructions": "Napisz tutaj swoją przecenę...",
|
||||||
|
"preview": "Zapowiedź"
|
||||||
},
|
},
|
||||||
"worldtravel": {
|
"worldtravel": {
|
||||||
"country_list": "Lista krajów",
|
"country_list": "Lista krajów",
|
||||||
|
|
|
@ -195,7 +195,9 @@
|
||||||
"emoji_picker": "Emoji-väljare",
|
"emoji_picker": "Emoji-väljare",
|
||||||
"hide": "Dölja",
|
"hide": "Dölja",
|
||||||
"show": "Visa",
|
"show": "Visa",
|
||||||
"download_calendar": "Ladda ner kalender"
|
"download_calendar": "Ladda ner kalender",
|
||||||
|
"md_instructions": "Skriv din avskrivning här...",
|
||||||
|
"preview": "Förhandsvisning"
|
||||||
},
|
},
|
||||||
"home": {
|
"home": {
|
||||||
"desc_1": "Upptäck, planera och utforska med lätthet",
|
"desc_1": "Upptäck, planera och utforska med lätthet",
|
||||||
|
|
|
@ -195,7 +195,9 @@
|
||||||
"emoji_picker": "表情符号选择器",
|
"emoji_picker": "表情符号选择器",
|
||||||
"hide": "隐藏",
|
"hide": "隐藏",
|
||||||
"show": "展示",
|
"show": "展示",
|
||||||
"download_calendar": "下载日历"
|
"download_calendar": "下载日历",
|
||||||
|
"md_instructions": "在这里写下你的标记...",
|
||||||
|
"preview": "预览"
|
||||||
},
|
},
|
||||||
"home": {
|
"home": {
|
||||||
"desc_1": "轻松发现、规划和探索",
|
"desc_1": "轻松发现、规划和探索",
|
||||||
|
|
|
@ -6,6 +6,11 @@
|
||||||
import Lost from '$lib/assets/undraw_lost.svg';
|
import Lost from '$lib/assets/undraw_lost.svg';
|
||||||
import { DefaultMarker, MapLibre, Popup } from 'svelte-maplibre';
|
import { DefaultMarker, MapLibre, Popup } from 'svelte-maplibre';
|
||||||
import { t } from 'svelte-i18n';
|
import { t } from 'svelte-i18n';
|
||||||
|
import { marked } from 'marked'; // Import the markdown parser
|
||||||
|
|
||||||
|
const renderMarkdown = (markdown: string) => {
|
||||||
|
return marked(markdown);
|
||||||
|
};
|
||||||
|
|
||||||
export let data: PageData;
|
export let data: PageData;
|
||||||
console.log(data);
|
console.log(data);
|
||||||
|
@ -244,11 +249,12 @@
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
{#if adventure.description}
|
{#if adventure.description}
|
||||||
<div class="grid gap-2">
|
<p class="text-sm text-muted-foreground" style="white-space: pre-wrap;"></p>
|
||||||
<p class="text-sm text-muted-foreground" style="white-space: pre-wrap;">
|
<article
|
||||||
{adventure.description}
|
class="prose overflow-auto h-full max-w-full p-4 border border-base-300 rounded-lg"
|
||||||
</p>
|
>
|
||||||
</div>
|
{@html renderMarkdown(adventure.description)}
|
||||||
|
</article>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue