markdown-text-editor 0.0.24-beta.3 → 0.1.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.
@@ -1,142 +1,142 @@
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
- "focus:outline-0",
40
- "border-0",
41
- "p-1.5",
42
- "max-w-full",
43
- "size-full",
44
- "bg-transparent",
45
- "outline-0",
46
- "appearance-none",
47
- "prose",
48
- "prose-sm",
49
- "md:prose-base",
50
- "dark:prose-invert",
51
- "text-stone-700",
52
- "dark:text-stone-200",
53
- "overflow-y-auto"
54
- );
55
- if (!this.usertextarea.hasAttribute('placeholder')) {
56
- this.usertextarea.placeholder = this.options.placeholder || 'Write your markdown...';
57
- }
58
- }
59
-
60
- buildEditorLayout() {
61
- this.editorContainer = document.createElement('div');
62
- this.editorContainer.className = `
63
- markdown-editor-wrapper
64
- border border-stone-200
65
- dark:border-stone-700
66
- rounded-md
67
- overflow-hidden
68
- `;
69
- this.usertextarea.parentNode.insertBefore(this.editorContainer, this.usertextarea);
70
-
71
- this.markdownEditorDiv = document.createElement('div');
72
- this.markdownEditorDiv.className = `editor-layout`;
73
- this.editorContainer.appendChild(this.markdownEditorDiv);
74
-
75
- this.addTextareaWrapper();
76
- if (this.preview) this.addPreviewWrapper();
77
- }
78
-
79
- addTextareaWrapper() {
80
- const textareaContainer = document.createElement('div');
81
- textareaContainer.className = `
82
- textarea-wrapper
83
- p-2
84
- bg-white
85
- dark:bg-stone-800
86
- grid
87
- after:px-3.5
88
- after:py-2.5
89
- after:text-inherit
90
- [&>textarea]:resize-none
91
- [&>textarea]:[grid-area:1/1/2/2]
92
- after:[grid-area:1/1/2/2]
93
- after:whitespace-pre-wrap
94
- after:invisible
95
- after:content-[attr(data-cloned-val)_'_']
96
- after:border
97
- `;
98
- textareaContainer.appendChild(this.usertextarea);
99
- this.markdownEditorDiv.appendChild(textareaContainer);
100
- }
101
-
102
-
103
-
104
- addPreviewWrapper() {
105
- const preview = new Preview(this.markdownEditorDiv);
106
- this.previewContent = preview.getPreviewContent();
107
- }
108
-
109
- addInputListener() {
110
- this.usertextarea.addEventListener('input', () => this.render());
111
- this.usertextarea.addEventListener('scroll', () => {
112
- const textarea = this.usertextarea;
113
- const previewPane = this.previewContent;
114
-
115
- // Calculate the proportion of the textarea that has been scrolled
116
- const textareaScrollRatio = textarea.scrollTop / (textarea.scrollHeight - textarea.clientHeight);
117
-
118
- // Apply the same scroll ratio to the preview pane
119
- previewPane.scrollTop = textareaScrollRatio * (previewPane.scrollHeight - previewPane.clientHeight);
120
- });
121
- }
122
-
123
- addToolbar() {
124
- new Toolbar(this, this.options.toolbar || ['bold', 'italic', 'strikethrough']);
125
- }
126
-
127
- insertText(text) {
128
- const { selectionStart, selectionEnd } = this.usertextarea;
129
- const value = this.usertextarea.value;
130
- this.usertextarea.value = `${value.substring(0, selectionStart)}${text}${value.substring(selectionEnd)}`;
131
- this.usertextarea.focus();
132
- this.usertextarea.setSelectionRange(selectionStart, selectionStart + text.length);
133
- this.render();
134
- }
135
-
136
- render() {
137
- const html = marked(this.usertextarea.value);
138
- this.previewContent.innerHTML = html;
139
- }
140
- }
141
-
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
+ "focus:outline-0",
40
+ "border-0",
41
+ "p-1.5",
42
+ "max-w-full",
43
+ "size-full",
44
+ "bg-transparent",
45
+ "outline-0",
46
+ "appearance-none",
47
+ "prose",
48
+ "prose-sm",
49
+ "md:prose-base",
50
+ "dark:prose-invert",
51
+ "text-stone-700",
52
+ "dark:text-stone-200",
53
+ "overflow-y-auto"
54
+ );
55
+ if (!this.usertextarea.hasAttribute('placeholder')) {
56
+ this.usertextarea.placeholder = this.options.placeholder || 'Write your markdown...';
57
+ }
58
+ }
59
+
60
+ buildEditorLayout() {
61
+ this.editorContainer = document.createElement('div');
62
+ this.editorContainer.className = `
63
+ markdown-editor-wrapper
64
+ border border-stone-200
65
+ dark:border-stone-700
66
+ rounded-md
67
+ overflow-hidden
68
+ `;
69
+ this.usertextarea.parentNode.insertBefore(this.editorContainer, this.usertextarea);
70
+
71
+ this.markdownEditorDiv = document.createElement('div');
72
+ this.markdownEditorDiv.className = `editor-layout`;
73
+ this.editorContainer.appendChild(this.markdownEditorDiv);
74
+
75
+ this.addTextareaWrapper();
76
+ if (this.preview) this.addPreviewWrapper();
77
+ }
78
+
79
+ addTextareaWrapper() {
80
+ const textareaContainer = document.createElement('div');
81
+ textareaContainer.className = `
82
+ textarea-wrapper
83
+ p-2
84
+ bg-white
85
+ dark:bg-stone-800
86
+ grid
87
+ after:px-3.5
88
+ after:py-2.5
89
+ after:text-inherit
90
+ [&>textarea]:resize-none
91
+ [&>textarea]:[grid-area:1/1/2/2]
92
+ after:[grid-area:1/1/2/2]
93
+ after:whitespace-pre-wrap
94
+ after:invisible
95
+ after:content-[attr(data-cloned-val)_'_']
96
+ after:border
97
+ `;
98
+ textareaContainer.appendChild(this.usertextarea);
99
+ this.markdownEditorDiv.appendChild(textareaContainer);
100
+ }
101
+
102
+
103
+
104
+ addPreviewWrapper() {
105
+ const preview = new Preview(this.markdownEditorDiv);
106
+ this.previewContent = preview.getPreviewContent();
107
+ }
108
+
109
+ addInputListener() {
110
+ this.usertextarea.addEventListener('input', () => this.render());
111
+ this.usertextarea.addEventListener('scroll', () => {
112
+ const textarea = this.usertextarea;
113
+ const previewPane = this.previewContent;
114
+
115
+ // Calculate the proportion of the textarea that has been scrolled
116
+ const textareaScrollRatio = textarea.scrollTop / (textarea.scrollHeight - textarea.clientHeight);
117
+
118
+ // Apply the same scroll ratio to the preview pane
119
+ previewPane.scrollTop = textareaScrollRatio * (previewPane.scrollHeight - previewPane.clientHeight);
120
+ });
121
+ }
122
+
123
+ addToolbar() {
124
+ new Toolbar(this, this.options.toolbar || ['bold', 'italic', 'strikethrough']);
125
+ }
126
+
127
+ insertText(text) {
128
+ const { selectionStart, selectionEnd } = this.usertextarea;
129
+ const value = this.usertextarea.value;
130
+ this.usertextarea.value = `${value.substring(0, selectionStart)}${text}${value.substring(selectionEnd)}`;
131
+ this.usertextarea.focus();
132
+ this.usertextarea.setSelectionRange(selectionStart, selectionStart + text.length);
133
+ this.render();
134
+ }
135
+
136
+ render() {
137
+ const html = marked(this.usertextarea.value);
138
+ this.previewContent.innerHTML = html;
139
+ }
140
+ }
141
+
142
142
  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-[90lvh] 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-[90lvh] 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
+ }
@@ -1,40 +0,0 @@
1
- // #components\Toolbar\tools\MarkdownTool.js
2
- class MarkdownTool {
3
- constructor(editor, syntax, defaultText) {
4
- this.editor = editor;
5
- this.syntax = syntax; // Markdown syntax (e.g., ** for bold, * for italic)
6
- this.defaultText = defaultText; // Default text if nothing is selected
7
- this.button = this.createButton();
8
- }
9
-
10
- // Create a button element (can be overridden in child classes)
11
- createButton(iconHtml) {
12
- const button = document.createElement('button');
13
- button.innerHTML = iconHtml; // Pass icon HTML from child classes
14
- button.type='button';
15
- button.className = 'markdown-btn p-2 hover:bg-stone-200 dark:hover:bg-stone-600 rounded duration-300';
16
- button.addEventListener('click', () => this.applySyntax());
17
- return button;
18
- }
19
-
20
- // Toggle markdown syntax at the current cursor position
21
- applySyntax() {
22
- const textarea = this.editor.usertextarea;
23
- const { selectionStart, selectionEnd } = textarea;
24
- const selectedText = textarea.value.substring(selectionStart, selectionEnd);
25
-
26
- const syntaxLength = this.syntax.length;
27
-
28
- if (selectedText.startsWith(this.syntax) && selectedText.endsWith(this.syntax)) {
29
- // If text is already wrapped with the markdown syntax, remove it
30
- const unformattedText = selectedText.slice(syntaxLength, -syntaxLength);
31
- this.editor.insertText(unformattedText);
32
- } else {
33
- // If no text is selected or it's not wrapped, add the markdown syntax
34
- const newText = `${this.syntax}${selectedText || this.defaultText}${this.syntax}`;
35
- this.editor.insertText(newText);
36
- }
37
- }
38
- }
39
-
40
- export default MarkdownTool;