markdown-text-editor 0.0.17-beta.1 → 0.0.18-beta.1
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/LICENSE +20 -20
- package/README.md +143 -143
- package/dist/index.css.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/markdown-text-editor.css.map +1 -1
- package/dist/markdown-text-editor.js.map +1 -1
- package/package.json +62 -61
- package/src/plugins/index.js +2 -2
- package/src/plugins/markdown/Toolbar/index.js +36 -36
- package/src/plugins/markdown/Toolbar/tools/BoldTool.js +14 -14
- package/src/plugins/markdown/Toolbar/tools/ItalicTool.js +16 -16
- package/src/plugins/markdown/Toolbar/tools/MarkdownTool.js +38 -38
- package/src/plugins/markdown/Toolbar/tools/PreviewTool.js +92 -92
- package/src/plugins/markdown/Toolbar/tools/StrikethroughTool.js +15 -15
- package/src/plugins/markdown/editor.js +139 -139
- package/src/plugins/markdown/preview.js +16 -16
- package/src/plugins/markdown/styles/main.css +2 -2
- package/src/plugins/markdown/utils/markdownUtils.js +7 -7
|
@@ -1,140 +1,140 @@
|
|
|
1
|
-
// #markdown\editor.js
|
|
2
|
-
import './styles/main.css';
|
|
3
|
-
import { marked } from 'marked';
|
|
4
|
-
import Toolbar from './Toolbar/index.js';
|
|
5
|
-
import Preview from './preview.js';
|
|
6
|
-
|
|
7
|
-
marked.setOptions({
|
|
8
|
-
breaks: true
|
|
9
|
-
});
|
|
10
|
-
|
|
11
|
-
class markdownEditor {
|
|
12
|
-
constructor(selector, options = {}) {
|
|
13
|
-
this.usertextarea = typeof selector === 'string' ? document.querySelector(selector) : selector;
|
|
14
|
-
this.options = options;
|
|
15
|
-
this.preview = this.options.toolbar.includes('preview');
|
|
16
|
-
this.init();
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
init() {
|
|
20
|
-
this.createEditor();
|
|
21
|
-
if (this.options.toolbar) this.addToolbar();
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
createEditor() {
|
|
25
|
-
if (!this.isTextareaValid()) return;
|
|
26
|
-
|
|
27
|
-
this.applyDefaultAttributes();
|
|
28
|
-
this.buildEditorLayout();
|
|
29
|
-
this.addInputListener();
|
|
30
|
-
this.render();
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
isTextareaValid() {
|
|
34
|
-
return this.usertextarea.tagName === 'TEXTAREA';
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
applyDefaultAttributes() {
|
|
38
|
-
this.usertextarea.classList.add(
|
|
39
|
-
"p-1.5",
|
|
40
|
-
"max-w-full",
|
|
41
|
-
"size-full",
|
|
42
|
-
"bg-transparent",
|
|
43
|
-
"outline-0",
|
|
44
|
-
"appearance-none",
|
|
45
|
-
"prose",
|
|
46
|
-
"prose-sm",
|
|
47
|
-
"md:prose-base",
|
|
48
|
-
"dark:prose-invert",
|
|
49
|
-
"text-stone-700",
|
|
50
|
-
"dark:text-stone-200",
|
|
51
|
-
"overflow-y-auto"
|
|
52
|
-
);
|
|
53
|
-
if (!this.usertextarea.hasAttribute('placeholder')) {
|
|
54
|
-
this.usertextarea.placeholder = this.options.placeholder || 'Write your markdown...';
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
buildEditorLayout() {
|
|
59
|
-
this.editorContainer = document.createElement('div');
|
|
60
|
-
this.editorContainer.className = `
|
|
61
|
-
markdown-editor-wrapper
|
|
62
|
-
border border-stone-200
|
|
63
|
-
dark:border-stone-700
|
|
64
|
-
rounded-md
|
|
65
|
-
overflow-hidden
|
|
66
|
-
`;
|
|
67
|
-
this.usertextarea.parentNode.insertBefore(this.editorContainer, this.usertextarea);
|
|
68
|
-
|
|
69
|
-
this.markdownEditorDiv = document.createElement('div');
|
|
70
|
-
this.markdownEditorDiv.className = `editor-layout`;
|
|
71
|
-
this.editorContainer.appendChild(this.markdownEditorDiv);
|
|
72
|
-
|
|
73
|
-
this.addTextareaWrapper();
|
|
74
|
-
if (this.preview) this.addPreviewWrapper();
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
addTextareaWrapper() {
|
|
78
|
-
const textareaContainer = document.createElement('div');
|
|
79
|
-
textareaContainer.className = `
|
|
80
|
-
textarea-wrapper
|
|
81
|
-
p-2
|
|
82
|
-
bg-white
|
|
83
|
-
dark:bg-stone-800
|
|
84
|
-
grid
|
|
85
|
-
after:px-3.5
|
|
86
|
-
after:py-2.5
|
|
87
|
-
after:text-inherit
|
|
88
|
-
[&>textarea]:resize-none
|
|
89
|
-
[&>textarea]:[grid-area:1/1/2/2]
|
|
90
|
-
after:[grid-area:1/1/2/2]
|
|
91
|
-
after:whitespace-pre-wrap
|
|
92
|
-
after:invisible
|
|
93
|
-
after:content-[attr(data-cloned-val)_'_']
|
|
94
|
-
after:border
|
|
95
|
-
`;
|
|
96
|
-
textareaContainer.appendChild(this.usertextarea);
|
|
97
|
-
this.markdownEditorDiv.appendChild(textareaContainer);
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
addPreviewWrapper() {
|
|
103
|
-
const preview = new Preview(this.markdownEditorDiv);
|
|
104
|
-
this.previewContent = preview.getPreviewContent();
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
addInputListener() {
|
|
108
|
-
this.usertextarea.addEventListener('input', () => this.render());
|
|
109
|
-
this.usertextarea.addEventListener('scroll', () => {
|
|
110
|
-
const textarea = this.usertextarea;
|
|
111
|
-
const previewPane = this.previewContent;
|
|
112
|
-
|
|
113
|
-
// Calculate the proportion of the textarea that has been scrolled
|
|
114
|
-
const textareaScrollRatio = textarea.scrollTop / (textarea.scrollHeight - textarea.clientHeight);
|
|
115
|
-
|
|
116
|
-
// Apply the same scroll ratio to the preview pane
|
|
117
|
-
previewPane.scrollTop = textareaScrollRatio * (previewPane.scrollHeight - previewPane.clientHeight);
|
|
118
|
-
});
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
addToolbar() {
|
|
122
|
-
new Toolbar(this, this.options.toolbar || ['bold', 'italic', 'strikethrough']);
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
insertText(text) {
|
|
126
|
-
const { selectionStart, selectionEnd } = this.usertextarea;
|
|
127
|
-
const value = this.usertextarea.value;
|
|
128
|
-
this.usertextarea.value = `${value.substring(0, selectionStart)}${text}${value.substring(selectionEnd)}`;
|
|
129
|
-
this.usertextarea.focus();
|
|
130
|
-
this.usertextarea.setSelectionRange(selectionStart, selectionStart + text.length);
|
|
131
|
-
this.render();
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
render() {
|
|
135
|
-
const html = marked(this.usertextarea.value);
|
|
136
|
-
this.previewContent.innerHTML = html;
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
|
|
1
|
+
// #markdown\editor.js
|
|
2
|
+
import './styles/main.css';
|
|
3
|
+
import { marked } from 'marked';
|
|
4
|
+
import Toolbar from './Toolbar/index.js';
|
|
5
|
+
import Preview from './preview.js';
|
|
6
|
+
|
|
7
|
+
marked.setOptions({
|
|
8
|
+
breaks: true
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
class markdownEditor {
|
|
12
|
+
constructor(selector, options = {}) {
|
|
13
|
+
this.usertextarea = typeof selector === 'string' ? document.querySelector(selector) : selector;
|
|
14
|
+
this.options = options;
|
|
15
|
+
this.preview = this.options.toolbar.includes('preview');
|
|
16
|
+
this.init();
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
init() {
|
|
20
|
+
this.createEditor();
|
|
21
|
+
if (this.options.toolbar) this.addToolbar();
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
createEditor() {
|
|
25
|
+
if (!this.isTextareaValid()) return;
|
|
26
|
+
|
|
27
|
+
this.applyDefaultAttributes();
|
|
28
|
+
this.buildEditorLayout();
|
|
29
|
+
this.addInputListener();
|
|
30
|
+
this.render();
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
isTextareaValid() {
|
|
34
|
+
return this.usertextarea.tagName === 'TEXTAREA';
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
applyDefaultAttributes() {
|
|
38
|
+
this.usertextarea.classList.add(
|
|
39
|
+
"p-1.5",
|
|
40
|
+
"max-w-full",
|
|
41
|
+
"size-full",
|
|
42
|
+
"bg-transparent",
|
|
43
|
+
"outline-0",
|
|
44
|
+
"appearance-none",
|
|
45
|
+
"prose",
|
|
46
|
+
"prose-sm",
|
|
47
|
+
"md:prose-base",
|
|
48
|
+
"dark:prose-invert",
|
|
49
|
+
"text-stone-700",
|
|
50
|
+
"dark:text-stone-200",
|
|
51
|
+
"overflow-y-auto"
|
|
52
|
+
);
|
|
53
|
+
if (!this.usertextarea.hasAttribute('placeholder')) {
|
|
54
|
+
this.usertextarea.placeholder = this.options.placeholder || 'Write your markdown...';
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
buildEditorLayout() {
|
|
59
|
+
this.editorContainer = document.createElement('div');
|
|
60
|
+
this.editorContainer.className = `
|
|
61
|
+
markdown-editor-wrapper
|
|
62
|
+
border border-stone-200
|
|
63
|
+
dark:border-stone-700
|
|
64
|
+
rounded-md
|
|
65
|
+
overflow-hidden
|
|
66
|
+
`;
|
|
67
|
+
this.usertextarea.parentNode.insertBefore(this.editorContainer, this.usertextarea);
|
|
68
|
+
|
|
69
|
+
this.markdownEditorDiv = document.createElement('div');
|
|
70
|
+
this.markdownEditorDiv.className = `editor-layout`;
|
|
71
|
+
this.editorContainer.appendChild(this.markdownEditorDiv);
|
|
72
|
+
|
|
73
|
+
this.addTextareaWrapper();
|
|
74
|
+
if (this.preview) this.addPreviewWrapper();
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
addTextareaWrapper() {
|
|
78
|
+
const textareaContainer = document.createElement('div');
|
|
79
|
+
textareaContainer.className = `
|
|
80
|
+
textarea-wrapper
|
|
81
|
+
p-2
|
|
82
|
+
bg-white
|
|
83
|
+
dark:bg-stone-800
|
|
84
|
+
grid
|
|
85
|
+
after:px-3.5
|
|
86
|
+
after:py-2.5
|
|
87
|
+
after:text-inherit
|
|
88
|
+
[&>textarea]:resize-none
|
|
89
|
+
[&>textarea]:[grid-area:1/1/2/2]
|
|
90
|
+
after:[grid-area:1/1/2/2]
|
|
91
|
+
after:whitespace-pre-wrap
|
|
92
|
+
after:invisible
|
|
93
|
+
after:content-[attr(data-cloned-val)_'_']
|
|
94
|
+
after:border
|
|
95
|
+
`;
|
|
96
|
+
textareaContainer.appendChild(this.usertextarea);
|
|
97
|
+
this.markdownEditorDiv.appendChild(textareaContainer);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
addPreviewWrapper() {
|
|
103
|
+
const preview = new Preview(this.markdownEditorDiv);
|
|
104
|
+
this.previewContent = preview.getPreviewContent();
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
addInputListener() {
|
|
108
|
+
this.usertextarea.addEventListener('input', () => this.render());
|
|
109
|
+
this.usertextarea.addEventListener('scroll', () => {
|
|
110
|
+
const textarea = this.usertextarea;
|
|
111
|
+
const previewPane = this.previewContent;
|
|
112
|
+
|
|
113
|
+
// Calculate the proportion of the textarea that has been scrolled
|
|
114
|
+
const textareaScrollRatio = textarea.scrollTop / (textarea.scrollHeight - textarea.clientHeight);
|
|
115
|
+
|
|
116
|
+
// Apply the same scroll ratio to the preview pane
|
|
117
|
+
previewPane.scrollTop = textareaScrollRatio * (previewPane.scrollHeight - previewPane.clientHeight);
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
addToolbar() {
|
|
122
|
+
new Toolbar(this, this.options.toolbar || ['bold', 'italic', 'strikethrough']);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
insertText(text) {
|
|
126
|
+
const { selectionStart, selectionEnd } = this.usertextarea;
|
|
127
|
+
const value = this.usertextarea.value;
|
|
128
|
+
this.usertextarea.value = `${value.substring(0, selectionStart)}${text}${value.substring(selectionEnd)}`;
|
|
129
|
+
this.usertextarea.focus();
|
|
130
|
+
this.usertextarea.setSelectionRange(selectionStart, selectionStart + text.length);
|
|
131
|
+
this.render();
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
render() {
|
|
135
|
+
const html = marked(this.usertextarea.value);
|
|
136
|
+
this.previewContent.innerHTML = html;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
140
|
export default markdownEditor;
|
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
// markdown\preview.js
|
|
2
|
-
class Preview {
|
|
3
|
-
constructor(container) {
|
|
4
|
-
this.previewContainer = document.createElement('div');
|
|
5
|
-
this.previewContainer.className = 'preview-wrapper bg-white dark:bg-stone-800 p-2 hidden';
|
|
6
|
-
this.previewContent = document.createElement('div');
|
|
7
|
-
this.previewContent.className = 'preview-content prose prose-sm md:prose-base dark:prose-invert p-1.5 overflow-y-auto h-lvh max-w-max';
|
|
8
|
-
this.previewContainer.appendChild(this.previewContent);
|
|
9
|
-
container.appendChild(this.previewContainer);
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
getPreviewContent() {
|
|
13
|
-
return this.previewContent;
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
|
|
1
|
+
// markdown\preview.js
|
|
2
|
+
class Preview {
|
|
3
|
+
constructor(container) {
|
|
4
|
+
this.previewContainer = document.createElement('div');
|
|
5
|
+
this.previewContainer.className = 'preview-wrapper bg-white dark:bg-stone-800 p-2 hidden';
|
|
6
|
+
this.previewContent = document.createElement('div');
|
|
7
|
+
this.previewContent.className = 'preview-content prose prose-sm md:prose-base dark:prose-invert p-1.5 overflow-y-auto h-lvh max-w-max';
|
|
8
|
+
this.previewContainer.appendChild(this.previewContent);
|
|
9
|
+
container.appendChild(this.previewContainer);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
getPreviewContent() {
|
|
13
|
+
return this.previewContent;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
17
|
export default Preview;
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
@tailwind base;
|
|
2
|
-
@tailwind components;
|
|
1
|
+
@tailwind base;
|
|
2
|
+
@tailwind components;
|
|
3
3
|
@tailwind utilities;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
// utils/markdownUtils.js
|
|
2
|
-
|
|
3
|
-
import { marked } from 'marked';
|
|
4
|
-
|
|
5
|
-
export function renderMarkdown(content) {
|
|
6
|
-
return marked(content);
|
|
7
|
-
}
|
|
1
|
+
// utils/markdownUtils.js
|
|
2
|
+
|
|
3
|
+
import { marked } from 'marked';
|
|
4
|
+
|
|
5
|
+
export function renderMarkdown(content) {
|
|
6
|
+
return marked(content);
|
|
7
|
+
}
|