@yannelli/live-markdown-vue 1.0.0
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/README.md +299 -0
- package/package.json +74 -0
package/README.md
ADDED
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
# Vue 3 Markdown Editor
|
|
2
|
+
|
|
3
|
+
A Vue 3 markdown editor component built as a drop-in replacement for textarea. Based on EasyMDE with full TypeScript support.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install live-markdown-vue
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
### Basic Usage
|
|
14
|
+
|
|
15
|
+
```vue
|
|
16
|
+
<script setup>
|
|
17
|
+
import { ref } from 'vue'
|
|
18
|
+
import { MarkdownEditor } from 'live-markdown-vue'
|
|
19
|
+
import 'live-markdown-vue/style.css'
|
|
20
|
+
|
|
21
|
+
const content = ref('')
|
|
22
|
+
</script>
|
|
23
|
+
|
|
24
|
+
<template>
|
|
25
|
+
<MarkdownEditor v-model="content" placeholder="Start writing..." />
|
|
26
|
+
</template>
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### With All Options
|
|
30
|
+
|
|
31
|
+
```vue
|
|
32
|
+
<script setup>
|
|
33
|
+
import { ref } from 'vue'
|
|
34
|
+
import { MarkdownEditor } from 'live-markdown-vue'
|
|
35
|
+
import 'live-markdown-vue/style.css'
|
|
36
|
+
|
|
37
|
+
const content = ref('# Hello World')
|
|
38
|
+
|
|
39
|
+
function handleUpload(file, onSuccess, onError) {
|
|
40
|
+
// Upload to your server
|
|
41
|
+
uploadToServer(file)
|
|
42
|
+
.then(url => onSuccess(url))
|
|
43
|
+
.catch(err => onError(err.message))
|
|
44
|
+
}
|
|
45
|
+
</script>
|
|
46
|
+
|
|
47
|
+
<template>
|
|
48
|
+
<MarkdownEditor
|
|
49
|
+
v-model="content"
|
|
50
|
+
placeholder="Start writing markdown..."
|
|
51
|
+
min-height="300px"
|
|
52
|
+
max-height="600px"
|
|
53
|
+
:autofocus="true"
|
|
54
|
+
:spellcheck="false"
|
|
55
|
+
:can-attach-files="true"
|
|
56
|
+
:file-attachments-max-size="5120"
|
|
57
|
+
:file-attachments-accepted-types="['image/png', 'image/jpeg', 'image/gif']"
|
|
58
|
+
:toolbar-buttons="[
|
|
59
|
+
['bold', 'italic', 'strike'],
|
|
60
|
+
['heading', 'blockquote', 'codeBlock'],
|
|
61
|
+
['bulletList', 'orderedList'],
|
|
62
|
+
['link', 'table'],
|
|
63
|
+
['undo', 'redo']
|
|
64
|
+
]"
|
|
65
|
+
direction="ltr"
|
|
66
|
+
@change="handleChange"
|
|
67
|
+
@blur="handleBlur"
|
|
68
|
+
@focus="handleFocus"
|
|
69
|
+
@upload="handleUpload"
|
|
70
|
+
/>
|
|
71
|
+
</template>
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Disabled State (Read-only HTML Preview)
|
|
75
|
+
|
|
76
|
+
```vue
|
|
77
|
+
<template>
|
|
78
|
+
<MarkdownEditor
|
|
79
|
+
v-model="content"
|
|
80
|
+
:disabled="true"
|
|
81
|
+
/>
|
|
82
|
+
</template>
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Props
|
|
86
|
+
|
|
87
|
+
| Prop | Type | Default | Description |
|
|
88
|
+
|------|------|---------|-------------|
|
|
89
|
+
| `modelValue` | `string` | `''` | The markdown content (v-model) |
|
|
90
|
+
| `disabled` | `boolean` | `false` | Renders as HTML when true |
|
|
91
|
+
| `placeholder` | `string` | `''` | Placeholder text |
|
|
92
|
+
| `minHeight` | `string` | `'200px'` | Minimum editor height |
|
|
93
|
+
| `maxHeight` | `string` | `undefined` | Maximum editor height |
|
|
94
|
+
| `autofocus` | `boolean` | `false` | Focus on mount |
|
|
95
|
+
| `spellcheck` | `boolean` | `false` | Enable browser spellcheck |
|
|
96
|
+
| `toolbarButtons` | `string[][]` | See below | Toolbar button groups |
|
|
97
|
+
| `canAttachFiles` | `boolean` | `false` | Enable file attachments |
|
|
98
|
+
| `fileAttachmentsMaxSize` | `number` | `undefined` | Max file size in KB |
|
|
99
|
+
| `fileAttachmentsAcceptedTypes` | `string[]` | `undefined` | Accepted MIME types |
|
|
100
|
+
| `debounceMs` | `number` | `300` | Debounce delay for changes |
|
|
101
|
+
| `translations` | `object` | English | Button translations |
|
|
102
|
+
| `direction` | `'ltr' \| 'rtl'` | `'ltr'` | Text direction |
|
|
103
|
+
|
|
104
|
+
### Default Toolbar Buttons
|
|
105
|
+
|
|
106
|
+
```javascript
|
|
107
|
+
[
|
|
108
|
+
['bold', 'italic', 'strike'],
|
|
109
|
+
['heading', 'blockquote', 'codeBlock'],
|
|
110
|
+
['bulletList', 'orderedList'],
|
|
111
|
+
['link', 'table'],
|
|
112
|
+
['undo', 'redo']
|
|
113
|
+
]
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Available Toolbar Buttons
|
|
117
|
+
|
|
118
|
+
- `bold` - Bold text
|
|
119
|
+
- `italic` - Italic text
|
|
120
|
+
- `strike` - Strikethrough text
|
|
121
|
+
- `heading` - Cycle through heading levels
|
|
122
|
+
- `blockquote` - Block quote
|
|
123
|
+
- `codeBlock` - Code block
|
|
124
|
+
- `bulletList` - Unordered list
|
|
125
|
+
- `orderedList` - Ordered list
|
|
126
|
+
- `link` - Insert link
|
|
127
|
+
- `table` - Insert table
|
|
128
|
+
- `attachFiles` - Attach files (requires `canAttachFiles`)
|
|
129
|
+
- `undo` - Undo
|
|
130
|
+
- `redo` - Redo
|
|
131
|
+
- `preview` - Toggle preview
|
|
132
|
+
- `sideBySide` - Side-by-side edit/preview
|
|
133
|
+
- `fullscreen` - Fullscreen mode
|
|
134
|
+
|
|
135
|
+
## Events
|
|
136
|
+
|
|
137
|
+
| Event | Payload | Description |
|
|
138
|
+
|-------|---------|-------------|
|
|
139
|
+
| `update:modelValue` | `string` | Emitted when content changes |
|
|
140
|
+
| `change` | `string` | Debounced content change |
|
|
141
|
+
| `blur` | - | Editor lost focus |
|
|
142
|
+
| `focus` | - | Editor gained focus |
|
|
143
|
+
| `upload` | `(file, onSuccess, onError)` | File upload requested |
|
|
144
|
+
|
|
145
|
+
## Exposed Methods
|
|
146
|
+
|
|
147
|
+
Access via template ref:
|
|
148
|
+
|
|
149
|
+
```vue
|
|
150
|
+
<script setup>
|
|
151
|
+
import { ref } from 'vue'
|
|
152
|
+
|
|
153
|
+
const editorRef = ref()
|
|
154
|
+
|
|
155
|
+
function focusEditor() {
|
|
156
|
+
editorRef.value?.focus()
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function getContent() {
|
|
160
|
+
return editorRef.value?.getValue()
|
|
161
|
+
}
|
|
162
|
+
</script>
|
|
163
|
+
|
|
164
|
+
<template>
|
|
165
|
+
<MarkdownEditor ref="editorRef" v-model="content" />
|
|
166
|
+
</template>
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
| Method | Returns | Description |
|
|
170
|
+
|--------|---------|-------------|
|
|
171
|
+
| `focus()` | - | Focus the editor |
|
|
172
|
+
| `blur()` | - | Blur the editor |
|
|
173
|
+
| `getValue()` | `string` | Get current content |
|
|
174
|
+
| `setValue(value)` | - | Set content |
|
|
175
|
+
| `getEditor()` | `EasyMDE \| null` | Get EasyMDE instance |
|
|
176
|
+
|
|
177
|
+
## Customization
|
|
178
|
+
|
|
179
|
+
### Custom Translations
|
|
180
|
+
|
|
181
|
+
```vue
|
|
182
|
+
<MarkdownEditor
|
|
183
|
+
v-model="content"
|
|
184
|
+
:translations="{
|
|
185
|
+
bold: 'Fett',
|
|
186
|
+
italic: 'Kursiv',
|
|
187
|
+
strike: 'Durchgestrichen',
|
|
188
|
+
link: 'Link',
|
|
189
|
+
heading: 'Überschrift',
|
|
190
|
+
blockquote: 'Zitat',
|
|
191
|
+
codeBlock: 'Code',
|
|
192
|
+
bulletList: 'Liste',
|
|
193
|
+
orderedList: 'Nummerierte Liste',
|
|
194
|
+
table: 'Tabelle',
|
|
195
|
+
attachFiles: 'Dateien',
|
|
196
|
+
undo: 'Rückgängig',
|
|
197
|
+
redo: 'Wiederholen',
|
|
198
|
+
preview: 'Vorschau',
|
|
199
|
+
sideBySide: 'Nebeneinander',
|
|
200
|
+
fullscreen: 'Vollbild'
|
|
201
|
+
}"
|
|
202
|
+
/>
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
### Dark Mode
|
|
206
|
+
|
|
207
|
+
The editor supports dark mode through multiple methods:
|
|
208
|
+
|
|
209
|
+
**1. Parent `.dark` class (Tailwind CSS compatible):**
|
|
210
|
+
|
|
211
|
+
```vue
|
|
212
|
+
<template>
|
|
213
|
+
<div class="dark">
|
|
214
|
+
<MarkdownEditor v-model="content" />
|
|
215
|
+
</div>
|
|
216
|
+
</template>
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
**2. Direct class on container:**
|
|
220
|
+
|
|
221
|
+
```vue
|
|
222
|
+
<template>
|
|
223
|
+
<MarkdownEditor class="dark" v-model="content" />
|
|
224
|
+
</template>
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
**3. Automatic system detection:**
|
|
228
|
+
|
|
229
|
+
The editor automatically detects `prefers-color-scheme: dark` and applies dark mode styles.
|
|
230
|
+
|
|
231
|
+
**Force light mode:**
|
|
232
|
+
|
|
233
|
+
To force light mode when the system prefers dark, add the `.light` class:
|
|
234
|
+
|
|
235
|
+
```vue
|
|
236
|
+
<template>
|
|
237
|
+
<MarkdownEditor class="light" v-model="content" />
|
|
238
|
+
</template>
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
### CSS Customization
|
|
242
|
+
|
|
243
|
+
The component uses CSS custom properties for theming. Override these variables to customize colors:
|
|
244
|
+
|
|
245
|
+
```css
|
|
246
|
+
.markdown-editor-container {
|
|
247
|
+
--mde-bg: #ffffff;
|
|
248
|
+
--mde-bg-secondary: #f8fafc;
|
|
249
|
+
--mde-bg-tertiary: #f3f4f6;
|
|
250
|
+
--mde-bg-code: #1f2937;
|
|
251
|
+
--mde-border: #e2e8f0;
|
|
252
|
+
--mde-border-light: #e5e7eb;
|
|
253
|
+
--mde-text: #374151;
|
|
254
|
+
--mde-text-muted: #6b7280;
|
|
255
|
+
--mde-text-toolbar: #64748b;
|
|
256
|
+
--mde-text-code: #f9fafb;
|
|
257
|
+
--mde-link: #2563eb;
|
|
258
|
+
--mde-link-hover: #1d4ed8;
|
|
259
|
+
--mde-hover: #e2e8f0;
|
|
260
|
+
--mde-cursor: #000000;
|
|
261
|
+
}
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
You can also override specific styles:
|
|
265
|
+
|
|
266
|
+
```css
|
|
267
|
+
/* Override editor styles */
|
|
268
|
+
.markdown-editor-container .EasyMDEContainer {
|
|
269
|
+
border-color: #your-color;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
.markdown-editor-container .editor-toolbar {
|
|
273
|
+
background-color: #your-bg;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
.markdown-editor-container .CodeMirror {
|
|
277
|
+
font-family: 'Your Font', monospace;
|
|
278
|
+
}
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
## Development
|
|
282
|
+
|
|
283
|
+
```bash
|
|
284
|
+
# Install dependencies
|
|
285
|
+
npm install
|
|
286
|
+
|
|
287
|
+
# Start dev server
|
|
288
|
+
npm run dev
|
|
289
|
+
|
|
290
|
+
# Run tests
|
|
291
|
+
npm test
|
|
292
|
+
|
|
293
|
+
# Build for production
|
|
294
|
+
npm run build
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
## License
|
|
298
|
+
|
|
299
|
+
MIT
|
package/package.json
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@yannelli/live-markdown-vue",
|
|
3
|
+
"author": "Ryan Yannelli <ryanyannelli@gmail.com> (https://ryanyannelli.com)",
|
|
4
|
+
"homepage": "https://github.com/yannelli/live-markdown-vue",
|
|
5
|
+
"version": "1.0.0",
|
|
6
|
+
"description": "A Vue 3 markdown editor component with live preview, built as a drop-in replacement for textarea",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"main": "./dist/live-markdown-vue.umd.cjs",
|
|
9
|
+
"module": "./dist/live-markdown-vue.js",
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"import": "./dist/live-markdown-vue.js",
|
|
14
|
+
"require": "./dist/live-markdown-vue.umd.cjs",
|
|
15
|
+
"types": "./dist/index.d.ts"
|
|
16
|
+
},
|
|
17
|
+
"./style.css": "./dist/style.css"
|
|
18
|
+
},
|
|
19
|
+
"files": [
|
|
20
|
+
"dist"
|
|
21
|
+
],
|
|
22
|
+
"scripts": {
|
|
23
|
+
"dev": "vite",
|
|
24
|
+
"build": "vue-tsc -b && vite build",
|
|
25
|
+
"preview": "vite preview",
|
|
26
|
+
"test": "vitest",
|
|
27
|
+
"test:run": "vitest run",
|
|
28
|
+
"test:coverage": "vitest run --coverage",
|
|
29
|
+
"lint": "eslint . --ext .vue,.js,.ts,.tsx --fix",
|
|
30
|
+
"type-check": "vue-tsc --noEmit"
|
|
31
|
+
},
|
|
32
|
+
"dependencies": {
|
|
33
|
+
"dompurify": "^3.2.3",
|
|
34
|
+
"easymde": "^2.18.0",
|
|
35
|
+
"marked": "^15.0.4"
|
|
36
|
+
},
|
|
37
|
+
"peerDependencies": {
|
|
38
|
+
"vue": "^3.4.0"
|
|
39
|
+
},
|
|
40
|
+
"devDependencies": {
|
|
41
|
+
"@types/dompurify": "^3.0.5",
|
|
42
|
+
"@types/node": "^22.10.2",
|
|
43
|
+
"@vitejs/plugin-vue": "^5.2.1",
|
|
44
|
+
"@vitest/coverage-v8": "^4.0.16",
|
|
45
|
+
"@vue/test-utils": "^2.4.6",
|
|
46
|
+
"happy-dom": "^20.0.11",
|
|
47
|
+
"typescript": "~5.7.2",
|
|
48
|
+
"vite": "^6.0.5",
|
|
49
|
+
"vite-plugin-dts": "^4.3.0",
|
|
50
|
+
"vitest": "^4.0.16",
|
|
51
|
+
"vue": "^3.5.13",
|
|
52
|
+
"vue-tsc": "^2.2.0"
|
|
53
|
+
},
|
|
54
|
+
"keywords": [
|
|
55
|
+
"vue",
|
|
56
|
+
"vue3",
|
|
57
|
+
"markdown",
|
|
58
|
+
"editor",
|
|
59
|
+
"textarea",
|
|
60
|
+
"codemirror",
|
|
61
|
+
"easymde"
|
|
62
|
+
],
|
|
63
|
+
"license": "MIT",
|
|
64
|
+
"repository": {
|
|
65
|
+
"type": "git",
|
|
66
|
+
"url": "git+https://github.com/yannelli/live-markdown-vue.git"
|
|
67
|
+
},
|
|
68
|
+
"directories": {
|
|
69
|
+
"test": "tests"
|
|
70
|
+
},
|
|
71
|
+
"bugs": {
|
|
72
|
+
"url": "https://github.com/yannelli/live-markdown-vue/issues"
|
|
73
|
+
}
|
|
74
|
+
}
|