@d34dman/flowdrop 0.0.24 → 0.0.25
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/form/FormCodeEditor.svelte +415 -0
- package/dist/components/form/FormCodeEditor.svelte.d.ts +23 -0
- package/dist/components/form/FormField.svelte +124 -73
- package/dist/components/form/FormField.svelte.d.ts +1 -1
- package/dist/components/form/FormMarkdownEditor.svelte +553 -0
- package/dist/components/form/FormMarkdownEditor.svelte.d.ts +29 -0
- package/dist/components/form/FormTemplateEditor.svelte +463 -0
- package/dist/components/form/FormTemplateEditor.svelte.d.ts +25 -0
- package/dist/components/form/index.d.ts +3 -0
- package/dist/components/form/index.js +3 -0
- package/dist/components/form/types.d.ts +54 -1
- package/package.json +9 -3
|
@@ -0,0 +1,553 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
FormMarkdownEditor Component
|
|
3
|
+
EasyMDE-based Markdown editor for rich text content
|
|
4
|
+
|
|
5
|
+
Features:
|
|
6
|
+
- Full Markdown editing with EasyMDE (fork of SimpleMDE)
|
|
7
|
+
- Live preview with syntax highlighting
|
|
8
|
+
- Toolbar with common formatting options
|
|
9
|
+
- Autosave support (optional)
|
|
10
|
+
- Spell checking
|
|
11
|
+
- Consistent styling with other form components
|
|
12
|
+
- Proper ARIA attributes for accessibility
|
|
13
|
+
- SSR-safe: Only loads EasyMDE on the client side
|
|
14
|
+
|
|
15
|
+
Usage:
|
|
16
|
+
Use with schema format: "markdown" to render this editor
|
|
17
|
+
-->
|
|
18
|
+
|
|
19
|
+
<script lang="ts">
|
|
20
|
+
import { onMount, onDestroy } from 'svelte';
|
|
21
|
+
import { browser } from '$app/environment';
|
|
22
|
+
import type EasyMDEConstructor from 'easymde';
|
|
23
|
+
|
|
24
|
+
/** EasyMDE instance type */
|
|
25
|
+
type EasyMDEInstance = EasyMDEConstructor;
|
|
26
|
+
|
|
27
|
+
interface Props {
|
|
28
|
+
/** Field identifier */
|
|
29
|
+
id: string;
|
|
30
|
+
/** Current value (markdown string) */
|
|
31
|
+
value: string;
|
|
32
|
+
/** Placeholder text shown when empty */
|
|
33
|
+
placeholder?: string;
|
|
34
|
+
/** Whether the field is required */
|
|
35
|
+
required?: boolean;
|
|
36
|
+
/** Editor height - "auto" or specific value like "300px" */
|
|
37
|
+
height?: string;
|
|
38
|
+
/** Whether to show the toolbar */
|
|
39
|
+
showToolbar?: boolean;
|
|
40
|
+
/** Whether to show the status bar */
|
|
41
|
+
showStatusBar?: boolean;
|
|
42
|
+
/** Whether to enable spell checking */
|
|
43
|
+
spellChecker?: boolean;
|
|
44
|
+
/** Whether to enable autosave */
|
|
45
|
+
autosave?: boolean;
|
|
46
|
+
/** Autosave delay in milliseconds */
|
|
47
|
+
autosaveDelay?: number;
|
|
48
|
+
/** ARIA description ID */
|
|
49
|
+
ariaDescribedBy?: string;
|
|
50
|
+
/** Callback when value changes */
|
|
51
|
+
onChange: (value: string) => void;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
let {
|
|
55
|
+
id,
|
|
56
|
+
value = '',
|
|
57
|
+
placeholder = 'Write your markdown here...',
|
|
58
|
+
required = false,
|
|
59
|
+
height = '300px',
|
|
60
|
+
showToolbar = true,
|
|
61
|
+
showStatusBar = true,
|
|
62
|
+
spellChecker = false,
|
|
63
|
+
autosave = false,
|
|
64
|
+
autosaveDelay = 10000,
|
|
65
|
+
ariaDescribedBy,
|
|
66
|
+
onChange
|
|
67
|
+
}: Props = $props();
|
|
68
|
+
|
|
69
|
+
/** Reference to the textarea element */
|
|
70
|
+
let textareaRef: HTMLTextAreaElement | undefined = $state(undefined);
|
|
71
|
+
|
|
72
|
+
/** EasyMDE editor instance */
|
|
73
|
+
let easymde: EasyMDEInstance | undefined = $state(undefined);
|
|
74
|
+
|
|
75
|
+
/** Loading state for the editor */
|
|
76
|
+
let isLoading = $state(true);
|
|
77
|
+
|
|
78
|
+
/** Flag to prevent update loops */
|
|
79
|
+
let isInternalUpdate = false;
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Custom toolbar configuration
|
|
83
|
+
* Provides a clean set of commonly used formatting options
|
|
84
|
+
*/
|
|
85
|
+
const toolbarConfig = [
|
|
86
|
+
'bold',
|
|
87
|
+
'italic',
|
|
88
|
+
'strikethrough',
|
|
89
|
+
'|',
|
|
90
|
+
'heading-1',
|
|
91
|
+
'heading-2',
|
|
92
|
+
'heading-3',
|
|
93
|
+
'|',
|
|
94
|
+
'quote',
|
|
95
|
+
'unordered-list',
|
|
96
|
+
'ordered-list',
|
|
97
|
+
'|',
|
|
98
|
+
'link',
|
|
99
|
+
'image',
|
|
100
|
+
'table',
|
|
101
|
+
'|',
|
|
102
|
+
'preview',
|
|
103
|
+
'side-by-side',
|
|
104
|
+
'fullscreen',
|
|
105
|
+
'|',
|
|
106
|
+
'guide'
|
|
107
|
+
] as const;
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Initialize EasyMDE editor on mount (client-side only)
|
|
111
|
+
*/
|
|
112
|
+
onMount(async () => {
|
|
113
|
+
// Only run in browser environment
|
|
114
|
+
if (!browser || !textareaRef) {
|
|
115
|
+
isLoading = false;
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
try {
|
|
120
|
+
// Dynamically import EasyMDE and its styles (client-side only)
|
|
121
|
+
const [EasyMDE] = await Promise.all([
|
|
122
|
+
import('easymde').then((m) => m.default),
|
|
123
|
+
import('easymde/dist/easymde.min.css')
|
|
124
|
+
]);
|
|
125
|
+
|
|
126
|
+
// Build autosave config if enabled
|
|
127
|
+
const autosaveConfig = autosave
|
|
128
|
+
? {
|
|
129
|
+
enabled: true,
|
|
130
|
+
uniqueId: `flowdrop-markdown-${id}`,
|
|
131
|
+
delay: autosaveDelay
|
|
132
|
+
}
|
|
133
|
+
: undefined;
|
|
134
|
+
|
|
135
|
+
// Create EasyMDE instance
|
|
136
|
+
easymde = new EasyMDE({
|
|
137
|
+
element: textareaRef,
|
|
138
|
+
initialValue: value,
|
|
139
|
+
placeholder: placeholder,
|
|
140
|
+
spellChecker: spellChecker,
|
|
141
|
+
autosave: autosaveConfig,
|
|
142
|
+
toolbar: showToolbar ? [...toolbarConfig] : false,
|
|
143
|
+
status: showStatusBar,
|
|
144
|
+
forceSync: true,
|
|
145
|
+
minHeight: height,
|
|
146
|
+
renderingConfig: {
|
|
147
|
+
singleLineBreaks: false,
|
|
148
|
+
codeSyntaxHighlighting: true
|
|
149
|
+
},
|
|
150
|
+
shortcuts: {
|
|
151
|
+
toggleBold: 'Cmd-B',
|
|
152
|
+
toggleItalic: 'Cmd-I',
|
|
153
|
+
toggleStrikethrough: 'Cmd-Alt-S',
|
|
154
|
+
toggleHeadingSmaller: 'Cmd-H',
|
|
155
|
+
toggleHeadingBigger: 'Shift-Cmd-H',
|
|
156
|
+
toggleCodeBlock: 'Cmd-Alt-C',
|
|
157
|
+
toggleBlockquote: "Cmd-'",
|
|
158
|
+
toggleOrderedList: 'Cmd-Alt-L',
|
|
159
|
+
toggleUnorderedList: 'Cmd-L',
|
|
160
|
+
cleanBlock: 'Cmd-E',
|
|
161
|
+
drawLink: 'Cmd-K',
|
|
162
|
+
drawImage: 'Cmd-Alt-I',
|
|
163
|
+
drawTable: 'Cmd-Alt-T',
|
|
164
|
+
togglePreview: 'Cmd-P',
|
|
165
|
+
toggleSideBySide: 'F9',
|
|
166
|
+
toggleFullScreen: 'F11'
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
// Listen for changes
|
|
171
|
+
easymde.codemirror.on('change', () => {
|
|
172
|
+
if (isInternalUpdate) {
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const newValue = easymde?.value() ?? '';
|
|
177
|
+
onChange(newValue);
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
isLoading = false;
|
|
181
|
+
} catch (error) {
|
|
182
|
+
console.error('Failed to load EasyMDE:', error);
|
|
183
|
+
isLoading = false;
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Clean up editor on destroy
|
|
189
|
+
*/
|
|
190
|
+
onDestroy(() => {
|
|
191
|
+
if (easymde) {
|
|
192
|
+
easymde.toTextArea();
|
|
193
|
+
}
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Update editor content when value prop changes externally
|
|
198
|
+
*/
|
|
199
|
+
$effect(() => {
|
|
200
|
+
if (!easymde) {
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
const currentValue = easymde.value();
|
|
205
|
+
|
|
206
|
+
// Only update if content actually changed and wasn't from internal edit
|
|
207
|
+
if (value !== currentValue && !isInternalUpdate) {
|
|
208
|
+
isInternalUpdate = true;
|
|
209
|
+
easymde.value(value);
|
|
210
|
+
isInternalUpdate = false;
|
|
211
|
+
}
|
|
212
|
+
});
|
|
213
|
+
</script>
|
|
214
|
+
|
|
215
|
+
<div class="form-markdown-editor" style="--editor-height: {height}">
|
|
216
|
+
<!-- Hidden input for form submission compatibility -->
|
|
217
|
+
<input
|
|
218
|
+
type="hidden"
|
|
219
|
+
{id}
|
|
220
|
+
name={id}
|
|
221
|
+
{value}
|
|
222
|
+
aria-describedby={ariaDescribedBy}
|
|
223
|
+
aria-required={required}
|
|
224
|
+
/>
|
|
225
|
+
|
|
226
|
+
<!-- Loading state -->
|
|
227
|
+
{#if isLoading}
|
|
228
|
+
<div class="form-markdown-editor__loading">
|
|
229
|
+
<div class="form-markdown-editor__spinner"></div>
|
|
230
|
+
<span>Loading editor...</span>
|
|
231
|
+
</div>
|
|
232
|
+
{/if}
|
|
233
|
+
|
|
234
|
+
<!-- EasyMDE textarea container -->
|
|
235
|
+
<textarea
|
|
236
|
+
bind:this={textareaRef}
|
|
237
|
+
aria-label="Markdown editor"
|
|
238
|
+
class:form-markdown-editor__textarea--hidden={!isLoading && easymde}>{value}</textarea
|
|
239
|
+
>
|
|
240
|
+
</div>
|
|
241
|
+
|
|
242
|
+
<style>
|
|
243
|
+
.form-markdown-editor {
|
|
244
|
+
position: relative;
|
|
245
|
+
width: 100%;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/* Loading state */
|
|
249
|
+
.form-markdown-editor__loading {
|
|
250
|
+
display: flex;
|
|
251
|
+
align-items: center;
|
|
252
|
+
justify-content: center;
|
|
253
|
+
gap: 0.75rem;
|
|
254
|
+
padding: 2rem;
|
|
255
|
+
background-color: var(--color-ref-gray-50, #f9fafb);
|
|
256
|
+
border: 1px solid var(--color-ref-gray-200, #e5e7eb);
|
|
257
|
+
border-radius: 0.5rem;
|
|
258
|
+
color: var(--color-ref-gray-500, #6b7280);
|
|
259
|
+
font-size: 0.875rem;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
.form-markdown-editor__spinner {
|
|
263
|
+
width: 1.25rem;
|
|
264
|
+
height: 1.25rem;
|
|
265
|
+
border: 2px solid var(--color-ref-gray-200, #e5e7eb);
|
|
266
|
+
border-top-color: var(--color-ref-blue-500, #3b82f6);
|
|
267
|
+
border-radius: 50%;
|
|
268
|
+
animation: spin 0.8s linear infinite;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
@keyframes spin {
|
|
272
|
+
to {
|
|
273
|
+
transform: rotate(360deg);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/* Hide the raw textarea when editor is loaded */
|
|
278
|
+
.form-markdown-editor__textarea--hidden {
|
|
279
|
+
display: none;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/* Fallback textarea styling (shown during loading or if editor fails) */
|
|
283
|
+
.form-markdown-editor textarea:not(.form-markdown-editor__textarea--hidden) {
|
|
284
|
+
width: 100%;
|
|
285
|
+
min-height: var(--editor-height, 300px);
|
|
286
|
+
padding: 0.75rem;
|
|
287
|
+
border: 1px solid var(--color-ref-gray-200, #e5e7eb);
|
|
288
|
+
border-radius: 0.5rem;
|
|
289
|
+
font-family: 'JetBrains Mono', 'Fira Code', 'Monaco', 'Menlo', monospace;
|
|
290
|
+
font-size: 0.875rem;
|
|
291
|
+
line-height: 1.5;
|
|
292
|
+
resize: vertical;
|
|
293
|
+
background-color: var(--color-ref-gray-50, #f9fafb);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/* EasyMDE container styling */
|
|
297
|
+
.form-markdown-editor :global(.CodeMirror) {
|
|
298
|
+
border: 1px solid var(--color-ref-gray-200, #e5e7eb);
|
|
299
|
+
border-top: none;
|
|
300
|
+
border-radius: 0;
|
|
301
|
+
background-color: var(--color-ref-gray-50, #f9fafb);
|
|
302
|
+
font-family: 'JetBrains Mono', 'Fira Code', 'Monaco', 'Menlo', monospace;
|
|
303
|
+
font-size: 0.875rem;
|
|
304
|
+
min-height: var(--editor-height, 300px);
|
|
305
|
+
transition: border-color 0.2s cubic-bezier(0.4, 0, 0.2, 1);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/* Header styling inside the editor - keep sizes reasonable */
|
|
309
|
+
.form-markdown-editor :global(.cm-header-1) {
|
|
310
|
+
font-size: 1.25rem;
|
|
311
|
+
line-height: 1.4;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
.form-markdown-editor :global(.cm-header-2) {
|
|
315
|
+
font-size: 1.125rem;
|
|
316
|
+
line-height: 1.4;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
.form-markdown-editor :global(.cm-header-3) {
|
|
320
|
+
font-size: 1rem;
|
|
321
|
+
line-height: 1.4;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
.form-markdown-editor :global(.cm-header-4),
|
|
325
|
+
.form-markdown-editor :global(.cm-header-5),
|
|
326
|
+
.form-markdown-editor :global(.cm-header-6) {
|
|
327
|
+
font-size: 0.9375rem;
|
|
328
|
+
line-height: 1.4;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
/* Keep all headers in monospace and reasonable weight */
|
|
332
|
+
.form-markdown-editor :global(.cm-header) {
|
|
333
|
+
font-family: 'JetBrains Mono', 'Fira Code', 'Monaco', 'Menlo', monospace;
|
|
334
|
+
font-weight: 600;
|
|
335
|
+
color: var(--color-ref-gray-900, #111827);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
.form-markdown-editor :global(.CodeMirror:hover) {
|
|
339
|
+
border-color: var(--color-ref-gray-300, #d1d5db);
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
.form-markdown-editor :global(.CodeMirror-focused) {
|
|
343
|
+
border-color: var(--color-ref-blue-500, #3b82f6);
|
|
344
|
+
background-color: #ffffff;
|
|
345
|
+
box-shadow:
|
|
346
|
+
0 0 0 3px rgba(59, 130, 246, 0.12),
|
|
347
|
+
0 1px 2px rgba(0, 0, 0, 0.04);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
/* Editor wrapper */
|
|
351
|
+
.form-markdown-editor :global(.editor-toolbar) {
|
|
352
|
+
border: 1px solid var(--color-ref-gray-200, #e5e7eb);
|
|
353
|
+
border-bottom: none;
|
|
354
|
+
border-radius: 0.5rem 0.5rem 0 0;
|
|
355
|
+
background-color: var(--color-ref-gray-100, #f3f4f6);
|
|
356
|
+
padding: 0.5rem;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
.form-markdown-editor :global(.editor-toolbar::before),
|
|
360
|
+
.form-markdown-editor :global(.editor-toolbar::after) {
|
|
361
|
+
display: none;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
/* Toolbar buttons */
|
|
365
|
+
.form-markdown-editor :global(.editor-toolbar button) {
|
|
366
|
+
color: var(--color-ref-gray-600, #4b5563);
|
|
367
|
+
border: none;
|
|
368
|
+
border-radius: 0.375rem;
|
|
369
|
+
width: 2rem;
|
|
370
|
+
height: 2rem;
|
|
371
|
+
transition: all 0.15s ease;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
.form-markdown-editor :global(.editor-toolbar button:hover) {
|
|
375
|
+
background-color: var(--color-ref-gray-200, #e5e7eb);
|
|
376
|
+
color: var(--color-ref-gray-900, #111827);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
.form-markdown-editor :global(.editor-toolbar button.active) {
|
|
380
|
+
background-color: var(--color-ref-blue-100, #dbeafe);
|
|
381
|
+
color: var(--color-ref-blue-700, #1d4ed8);
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
/* Separator */
|
|
385
|
+
.form-markdown-editor :global(.editor-toolbar i.separator) {
|
|
386
|
+
border-left: 1px solid var(--color-ref-gray-300, #d1d5db);
|
|
387
|
+
margin: 0 0.25rem;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
/* Status bar */
|
|
391
|
+
.form-markdown-editor :global(.editor-statusbar) {
|
|
392
|
+
border: 1px solid var(--color-ref-gray-200, #e5e7eb);
|
|
393
|
+
border-top: none;
|
|
394
|
+
border-radius: 0 0 0.5rem 0.5rem;
|
|
395
|
+
background-color: var(--color-ref-gray-50, #f9fafb);
|
|
396
|
+
padding: 0.5rem 0.75rem;
|
|
397
|
+
font-size: 0.75rem;
|
|
398
|
+
color: var(--color-ref-gray-500, #6b7280);
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
/* Preview pane */
|
|
402
|
+
.form-markdown-editor :global(.editor-preview) {
|
|
403
|
+
background-color: #ffffff;
|
|
404
|
+
padding: 1rem;
|
|
405
|
+
font-family: inherit;
|
|
406
|
+
font-size: 0.875rem;
|
|
407
|
+
line-height: 1.6;
|
|
408
|
+
color: var(--color-ref-gray-900, #111827);
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
.form-markdown-editor :global(.editor-preview h1),
|
|
412
|
+
.form-markdown-editor :global(.editor-preview h2),
|
|
413
|
+
.form-markdown-editor :global(.editor-preview h3),
|
|
414
|
+
.form-markdown-editor :global(.editor-preview h4),
|
|
415
|
+
.form-markdown-editor :global(.editor-preview h5),
|
|
416
|
+
.form-markdown-editor :global(.editor-preview h6) {
|
|
417
|
+
margin-top: 1.5em;
|
|
418
|
+
margin-bottom: 0.5em;
|
|
419
|
+
font-weight: 600;
|
|
420
|
+
color: var(--color-ref-gray-900, #111827);
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
.form-markdown-editor :global(.editor-preview h1) {
|
|
424
|
+
font-size: 1.5rem;
|
|
425
|
+
border-bottom: 1px solid var(--color-ref-gray-200, #e5e7eb);
|
|
426
|
+
padding-bottom: 0.5rem;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
.form-markdown-editor :global(.editor-preview h2) {
|
|
430
|
+
font-size: 1.25rem;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
.form-markdown-editor :global(.editor-preview h3) {
|
|
434
|
+
font-size: 1.125rem;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
.form-markdown-editor :global(.editor-preview p) {
|
|
438
|
+
margin: 0.75em 0;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
.form-markdown-editor :global(.editor-preview code) {
|
|
442
|
+
padding: 0.125rem 0.375rem;
|
|
443
|
+
background-color: var(--color-ref-gray-100, #f3f4f6);
|
|
444
|
+
border-radius: 0.25rem;
|
|
445
|
+
font-family: 'JetBrains Mono', 'Fira Code', 'Monaco', 'Menlo', monospace;
|
|
446
|
+
font-size: 0.8125rem;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
.form-markdown-editor :global(.editor-preview pre) {
|
|
450
|
+
padding: 1rem;
|
|
451
|
+
background-color: var(--color-ref-gray-900, #111827);
|
|
452
|
+
border-radius: 0.5rem;
|
|
453
|
+
overflow-x: auto;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
.form-markdown-editor :global(.editor-preview pre code) {
|
|
457
|
+
padding: 0;
|
|
458
|
+
background-color: transparent;
|
|
459
|
+
color: var(--color-ref-gray-100, #f3f4f6);
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
.form-markdown-editor :global(.editor-preview blockquote) {
|
|
463
|
+
margin: 1rem 0;
|
|
464
|
+
padding: 0.5rem 1rem;
|
|
465
|
+
border-left: 4px solid var(--color-ref-blue-500, #3b82f6);
|
|
466
|
+
background-color: var(--color-ref-blue-50, #eff6ff);
|
|
467
|
+
color: var(--color-ref-gray-700, #374151);
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
.form-markdown-editor :global(.editor-preview ul),
|
|
471
|
+
.form-markdown-editor :global(.editor-preview ol) {
|
|
472
|
+
margin: 0.75em 0;
|
|
473
|
+
padding-left: 1.5rem;
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
.form-markdown-editor :global(.editor-preview li) {
|
|
477
|
+
margin: 0.25em 0;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
.form-markdown-editor :global(.editor-preview a) {
|
|
481
|
+
color: var(--color-ref-blue-600, #2563eb);
|
|
482
|
+
text-decoration: underline;
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
.form-markdown-editor :global(.editor-preview a:hover) {
|
|
486
|
+
color: var(--color-ref-blue-700, #1d4ed8);
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
.form-markdown-editor :global(.editor-preview table) {
|
|
490
|
+
width: 100%;
|
|
491
|
+
border-collapse: collapse;
|
|
492
|
+
margin: 1rem 0;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
.form-markdown-editor :global(.editor-preview th),
|
|
496
|
+
.form-markdown-editor :global(.editor-preview td) {
|
|
497
|
+
border: 1px solid var(--color-ref-gray-200, #e5e7eb);
|
|
498
|
+
padding: 0.5rem 0.75rem;
|
|
499
|
+
text-align: left;
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
.form-markdown-editor :global(.editor-preview th) {
|
|
503
|
+
background-color: var(--color-ref-gray-100, #f3f4f6);
|
|
504
|
+
font-weight: 600;
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
.form-markdown-editor :global(.editor-preview img) {
|
|
508
|
+
max-width: 100%;
|
|
509
|
+
border-radius: 0.5rem;
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
/* Side-by-side mode */
|
|
513
|
+
.form-markdown-editor :global(.CodeMirror-sided) {
|
|
514
|
+
width: 50% !important;
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
.form-markdown-editor :global(.editor-preview-side) {
|
|
518
|
+
width: 50%;
|
|
519
|
+
border: 1px solid var(--color-ref-gray-200, #e5e7eb);
|
|
520
|
+
border-left: none;
|
|
521
|
+
border-radius: 0 0 0.5rem 0;
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
/* Fullscreen mode adjustments */
|
|
525
|
+
.form-markdown-editor :global(.editor-toolbar.fullscreen) {
|
|
526
|
+
border-radius: 0;
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
.form-markdown-editor :global(.CodeMirror-fullscreen) {
|
|
530
|
+
border-radius: 0;
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
/* Placeholder */
|
|
534
|
+
.form-markdown-editor :global(.CodeMirror .CodeMirror-placeholder) {
|
|
535
|
+
color: var(--color-ref-gray-400, #9ca3af);
|
|
536
|
+
font-style: italic;
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
/* EasyMDE specific wrapper */
|
|
540
|
+
.form-markdown-editor :global(.EasyMDEContainer) {
|
|
541
|
+
width: 100%;
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
/* When no status bar, CodeMirror gets bottom rounded corners */
|
|
545
|
+
.form-markdown-editor :global(.EasyMDEContainer:not(:has(.editor-statusbar)) .CodeMirror) {
|
|
546
|
+
border-radius: 0 0 0.5rem 0.5rem;
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
/* When status bar exists, it gets the bottom rounded corners */
|
|
550
|
+
.form-markdown-editor :global(.EasyMDEContainer:has(.editor-statusbar) .CodeMirror) {
|
|
551
|
+
border-bottom: none;
|
|
552
|
+
}
|
|
553
|
+
</style>
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
interface Props {
|
|
2
|
+
/** Field identifier */
|
|
3
|
+
id: string;
|
|
4
|
+
/** Current value (markdown string) */
|
|
5
|
+
value: string;
|
|
6
|
+
/** Placeholder text shown when empty */
|
|
7
|
+
placeholder?: string;
|
|
8
|
+
/** Whether the field is required */
|
|
9
|
+
required?: boolean;
|
|
10
|
+
/** Editor height - "auto" or specific value like "300px" */
|
|
11
|
+
height?: string;
|
|
12
|
+
/** Whether to show the toolbar */
|
|
13
|
+
showToolbar?: boolean;
|
|
14
|
+
/** Whether to show the status bar */
|
|
15
|
+
showStatusBar?: boolean;
|
|
16
|
+
/** Whether to enable spell checking */
|
|
17
|
+
spellChecker?: boolean;
|
|
18
|
+
/** Whether to enable autosave */
|
|
19
|
+
autosave?: boolean;
|
|
20
|
+
/** Autosave delay in milliseconds */
|
|
21
|
+
autosaveDelay?: number;
|
|
22
|
+
/** ARIA description ID */
|
|
23
|
+
ariaDescribedBy?: string;
|
|
24
|
+
/** Callback when value changes */
|
|
25
|
+
onChange: (value: string) => void;
|
|
26
|
+
}
|
|
27
|
+
declare const FormMarkdownEditor: import("svelte").Component<Props, {}, "">;
|
|
28
|
+
type FormMarkdownEditor = ReturnType<typeof FormMarkdownEditor>;
|
|
29
|
+
export default FormMarkdownEditor;
|