frontend/src/lib/components/Markdown.svelte
MuslemRahimi 7af34469f4 ui fixes
2024-11-23 17:24:58 +01:00

199 lines
4.9 KiB
Svelte

<script lang="ts">
import { onDestroy, onMount } from "svelte";
import { markdownValue, markdownClicked } from "$lib/store";
import showdown from "showdown";
export let id;
export let outputCounter;
export let name;
const converter = new showdown.Converter();
const toolbarOptions = {
container: [
["bold"],
["italic"],
["link"],
["image"],
[{ list: "ordered" }, { list: "bullet" }],
[{ header: [1, 2, 3, 4, 5, 6, false] }],
],
};
let quill;
let html;
let value;
async function initializeQuillEditor() {
const container = document.getElementById(id);
try {
const { default: Quill } = await import("quill");
quill = new Quill(container, {
modules: {
toolbar: toolbarOptions,
},
theme: "snow",
placeholder: "Markdown your code here...",
keepFocus: true,
});
} catch (e) {
console.log(e);
}
quill.on("editor-change", function () {
const editor = quill.root;
// Add text-4xl class to all h1 elements
editor.querySelectorAll("h1").forEach((h1) => {
h1.classList.add("text-4xl");
});
editor.querySelectorAll("h2").forEach((h2) => {
h2.classList.add("text-3xl");
});
editor.querySelectorAll("h3").forEach((h3) => {
h3.classList.add("text-xl");
});
editor.querySelectorAll("h4").forEach((h4) => {
h4.classList.add("text-md");
});
editor.querySelectorAll("h5").forEach((h5) => {
h5.classList.add("text-sm");
});
editor.querySelectorAll("h6").forEach((h6) => {
h6.classList.add("text-xs");
});
editor.querySelectorAll("a").forEach((a) => {
a.classList.add("text-blue-400", "hover:text-white", "underline");
});
editor.querySelectorAll("ol").forEach((ol) => {
ol.classList.add("list-decimal", "ml-10", "mt-3");
});
editor.querySelectorAll("ul").forEach((ul) => {
ul.classList.add("list-disc", "ml-10", "mt-3");
});
$: value = editor.innerHTML;
const contents = editor.innerHTML;
markdownValue.update((value) => [
...value.filter((item) => item.id !== id),
{ id: id, name: name, input: contents },
]);
console.log($markdownValue);
// Force Svelte to update the DOM
$: {
}
});
// Convert the markdown value to HTML and set it as the content of the Quill editor
html = converter.makeHtml($markdownValue[id]?.input || "");
quill.setContents(quill.clipboard.convert(html));
}
onMount(async () => {
await initializeQuillEditor();
});
onDestroy(() => {
if (quill) {
quill.off("text-change");
quill = null;
}
});
/** Dispatch event on click outside of node */
function clickOutside(node) {
const handleClick = (event) => {
if (node && !node.contains(event.target) && !event.defaultPrevented) {
node.dispatchEvent(new CustomEvent("click_outside", node));
}
};
document.addEventListener("click", handleClick, true);
return {
destroy() {
document.removeEventListener("click", handleClick, true);
},
};
}
function handleClickOutside(event) {
$markdownClicked[id] = false;
}
function handleClick() {
$markdownClicked[id] = true;
}
function hasContent(html) {
const el = document.createElement("div");
el.innerHTML = html;
let content = el.firstChild.innerHTML.trim();
// Remove <br> tags
content = content.replace(/<br\s*\/?>/gi, "");
return content !== "";
}
function keyCombination(event) {
if (event.key === "Enter" && event.shiftKey) {
$markdownClicked[id] = false;
event.preventDefault();
}
}
$: {
if ($markdownClicked) {
console.log($markdownClicked);
}
}
</script>
<div
on:keydown={keyCombination}
on:click={handleClick}
use:clickOutside
on:click_outside={handleClickOutside}
class="text-gray-100 w-full sm:w-3/4 max-w-2xl mt-3 rounded-md"
>
<div
class="{$markdownClicked[id] === false
? 'hidden'
: ''} text-gray-100 w-full sm:w-3/4 max-w-2xl mb-4 rounded-md"
>
<textarea bind:value {id} class="bg-[#1A1A27] min-h-12 w-full" />
</div>
<div
class="{$markdownClicked[id] === true
? 'hidden'
: ''} text-white mt-3 cursor-pointer w-full sm:w-3/4 max-w-2xl text-sm p-2"
>
{#each $markdownValue as mk}
{#if mk?.id === id}
<div class="flex flex-col">
<span class="text-gray-400 mb-4">[{outputCounter}] : </span>
<div class="border-b border-gray-600 rounded-md">
{#if hasContent(value)}
{@html value}
{:else}
<p class="italic text-gray-400">Markdown your code here...</p>
{/if}
</div>
</div>
{/if}
{/each}
</div>
</div>
<style>
@import "/src/lib/assets/style_quill.css";
</style>