Reusable MarkdownEditor component using EasyMDE: - Toolbar: bold, italic, headings, quotes, lists, links, images, tables, code blocks, preview, side-by-side, fullscreen - Dark mode support with custom CSS overrides - Value syncs via bind:value for form submission - Applied to both wiki new and edit pages - Replaces the plain textarea + manual preview toggle Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,111 @@
|
||||
<script lang="ts">
|
||||
import { onMount, onDestroy } from 'svelte';
|
||||
import { browser } from '$app/environment';
|
||||
|
||||
interface Props {
|
||||
name?: string;
|
||||
value?: string;
|
||||
placeholder?: string;
|
||||
}
|
||||
|
||||
let { name = 'content', value = $bindable(''), placeholder = 'Write in Markdown...' }: Props = $props();
|
||||
|
||||
let textareaEl: HTMLTextAreaElement;
|
||||
let editor: any = null;
|
||||
|
||||
onMount(async () => {
|
||||
if (!browser) return;
|
||||
|
||||
const EasyMDE = (await import('easymde')).default;
|
||||
await import('easymde/dist/easymde.min.css');
|
||||
|
||||
editor = new EasyMDE({
|
||||
element: textareaEl,
|
||||
initialValue: value,
|
||||
spellChecker: false,
|
||||
autofocus: false,
|
||||
placeholder,
|
||||
status: false,
|
||||
minHeight: '300px',
|
||||
toolbar: [
|
||||
'bold', 'italic', 'heading', '|',
|
||||
'quote', 'unordered-list', 'ordered-list', '|',
|
||||
'link', 'image', 'table', 'code', '|',
|
||||
'preview', 'side-by-side', 'fullscreen', '|',
|
||||
'guide'
|
||||
],
|
||||
sideBySideFullscreen: false
|
||||
});
|
||||
|
||||
editor.codemirror.on('change', () => {
|
||||
value = editor.value();
|
||||
});
|
||||
});
|
||||
|
||||
onDestroy(() => {
|
||||
if (editor) {
|
||||
editor.toTextArea();
|
||||
editor = null;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="markdown-editor">
|
||||
<textarea bind:this={textareaEl} id={name}></textarea>
|
||||
<input type="hidden" {name} value={value} />
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.markdown-editor :global(.EasyMDEContainer) {
|
||||
border: none;
|
||||
}
|
||||
.markdown-editor :global(.EasyMDEContainer .CodeMirror) {
|
||||
border: 1px solid #d1d5db;
|
||||
border-radius: 0.375rem;
|
||||
font-family: ui-monospace, monospace;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
.markdown-editor :global(.editor-toolbar) {
|
||||
border: 1px solid #d1d5db;
|
||||
border-bottom: none;
|
||||
border-radius: 0.375rem 0.375rem 0 0;
|
||||
opacity: 1;
|
||||
}
|
||||
.markdown-editor :global(.editor-toolbar button) {
|
||||
color: #6b7280 !important;
|
||||
}
|
||||
.markdown-editor :global(.editor-toolbar button:hover) {
|
||||
background: #f3f4f6;
|
||||
}
|
||||
.markdown-editor :global(.editor-toolbar button.active) {
|
||||
background: #e5e7eb;
|
||||
}
|
||||
/* Dark mode */
|
||||
:global(.dark) .markdown-editor :global(.CodeMirror) {
|
||||
background: #374151;
|
||||
color: #f3f4f6;
|
||||
border-color: #4b5563;
|
||||
}
|
||||
:global(.dark) .markdown-editor :global(.editor-toolbar) {
|
||||
background: #1f2937;
|
||||
border-color: #4b5563;
|
||||
}
|
||||
:global(.dark) .markdown-editor :global(.editor-toolbar button) {
|
||||
color: #9ca3af !important;
|
||||
}
|
||||
:global(.dark) .markdown-editor :global(.editor-toolbar button:hover) {
|
||||
background: #374151;
|
||||
}
|
||||
:global(.dark) .markdown-editor :global(.CodeMirror-cursor) {
|
||||
border-color: #f3f4f6;
|
||||
}
|
||||
:global(.dark) .markdown-editor :global(.editor-preview) {
|
||||
background: #374151;
|
||||
color: #f3f4f6;
|
||||
}
|
||||
:global(.dark) .markdown-editor :global(.editor-preview-side) {
|
||||
background: #374151;
|
||||
color: #f3f4f6;
|
||||
border-color: #4b5563;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user