markdown-text-editor 0.0.25-beta.3 → 0.1.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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "markdown-text-editor",
3
- "version": "0.0.25-beta.3",
4
- "description": "A powerful, easy-to-use Markdown editor with a real-time preview, syntax highlighting. Ideal for developers, writers, and content creators who need a seamless, interactive writing experience with full Markdown support.",
3
+ "version": "0.1.1",
4
+ "description": "A simple JavaScript Markdown editor plugin with real-time preview, and easy integration.",
5
5
  "main": "./dist/markdown-text-editor.js",
6
6
  "browser": "dist/markdown-text-editor.js",
7
7
  "style": "./dist/markdown-text-editor.css",
@@ -11,7 +11,7 @@
11
11
  ],
12
12
  "repository": {
13
13
  "type": "git",
14
- "url": "https://github.com/nezanuha/markdown-text-editor"
14
+ "url": "git+https://github.com/nezanuha/markdown-text-editor.git"
15
15
  },
16
16
  "scripts": {
17
17
  "clean": "rimraf dist",
@@ -20,19 +20,26 @@
20
20
  "dev": "webpack serve --mode=development"
21
21
  },
22
22
  "publishConfig": {
23
- "registry": "https://registry.npmjs.org/"
23
+ "provenance": true,
24
+ "access": "public"
24
25
  },
25
26
  "keywords": [
26
- "markdown-text-editor",
27
27
  "markdown",
28
- "text-editor",
28
+ "mde",
29
+ "markdown js library",
29
30
  "editor",
30
- "live preview",
31
- "syntax highlighting",
32
- "developer tools",
33
- "content creation",
34
- "plugin",
35
- "open source"
31
+ "markdown editor",
32
+ "text editor",
33
+ "markdown preview",
34
+ "markdown syntax",
35
+ "markdown plugin",
36
+ "rich text editor",
37
+ "wysiwyg",
38
+ "markdown formatting",
39
+ "code editor",
40
+ "editor plugin",
41
+ "text formatting",
42
+ "markdown-text-editor"
36
43
  ],
37
44
  "author": "Nezanuha",
38
45
  "license": "MIT",
@@ -41,8 +48,8 @@
41
48
  "url": "https://github.com/nezanuha/markdown-text-editor/issues"
42
49
  },
43
50
  "dependencies": {
44
- "marked": "^15.0.4",
45
- "tailwindcss": "^3.4.13"
51
+ "marked": "~15.0.4",
52
+ "tailwindcss": "~3.4.13"
46
53
  },
47
54
  "devDependencies": {
48
55
  "@babel/core": "^7.25.2",
@@ -0,0 +1,66 @@
1
+ class MakeTool {
2
+ constructor(editor, syntax, title) {
3
+ this.editor = editor;
4
+ this.syntax = syntax; // Markdown syntax (e.g., ** for bold, * for italic)
5
+ this.defaultText = `${title} text`; // Default text if nothing is selected
6
+ this.button = this.createButton();
7
+ this.title = title
8
+ }
9
+
10
+ // Create a button element (can be overridden in child classes)
11
+ createButton(iconHtml) {
12
+ const button = document.createElement('button');
13
+ const buttonClass = this.title ? `${this.title.replace(/ /g, '-')}-btn`.toLowerCase() : '';
14
+ button.innerHTML = iconHtml; // Pass icon HTML from child classes
15
+ button.type = 'button';
16
+ button.title = this.title;
17
+ button.className = `markdown-btn ${buttonClass}${buttonClass == 'preview-btn' ? ' sticky right-0 bg-stone-100 dark:bg-stone-900 ' : ' ' }p-2 hover:bg-stone-200 dark:hover:bg-stone-600 rounded duration-300 text-stone-900 dark:text-stone-100`;
18
+ button.addEventListener('click', () => this.applySyntax('both')); // Default to 'both', can change in child
19
+ return button;
20
+ }
21
+
22
+ // Toggle markdown syntax at the current cursor position
23
+ applySyntax(position = 'both') {
24
+ const textarea = this.editor.usertextarea;
25
+ const { selectionStart, selectionEnd } = textarea;
26
+ const selectedText = textarea.value.substring(selectionStart, selectionEnd);
27
+
28
+ const syntaxLength = this.syntax.length;
29
+
30
+ // Check which type of syntax to apply based on the position
31
+ let newText = '';
32
+ if(position === 'start'){
33
+ if (selectedText.startsWith(this.syntax)) {
34
+ // If text is already wrapped with the markdown syntax, remove it
35
+ const unformattedText = selectedText.slice(syntaxLength);
36
+ this.editor.insertText(unformattedText);
37
+ }else{
38
+ newText = `${this.syntax} ${selectedText || this.defaultText}`;
39
+ // Insert the new formatted text
40
+ this.editor.insertText(newText);
41
+ }
42
+ } else if (position === 'end') {
43
+ if (selectedText.startsWith(this.syntax)) {
44
+ // If text is already wrapped with the markdown syntax, remove it
45
+ const unformattedText = selectedText.slice(syntaxLength);
46
+ this.editor.insertText(unformattedText);
47
+ }else{
48
+ newText = `${selectedText || this.defaultText}${this.syntax}`;
49
+ // Insert the new formatted text
50
+ this.editor.insertText(newText);
51
+ }
52
+ } else {
53
+ if (selectedText.startsWith(this.syntax) && selectedText.endsWith(this.syntax)) {
54
+ // If text is already wrapped with the markdown syntax, remove it
55
+ const unformattedText = selectedText.slice(syntaxLength, -syntaxLength);
56
+ this.editor.insertText(unformattedText);
57
+ }else{
58
+ newText = `${this.syntax}${selectedText || this.defaultText}${this.syntax}`;
59
+ // Insert the new formatted text
60
+ this.editor.insertText(newText);
61
+ }
62
+ }
63
+ }
64
+ }
65
+
66
+ export default MakeTool;
@@ -2,33 +2,47 @@
2
2
  import BoldTool from './tools/BoldTool.js';
3
3
  import ItalicTool from './tools/ItalicTool.js';
4
4
  import StrikethroughTool from './tools/StrikethroughTool.js';
5
+ import ULTool from './tools/ULTool.js';
6
+ import OLTool from './tools/OLTool.js';
5
7
  import PreviewTool from './tools/PreviewTool.js'
8
+ import CheckListTool from './tools/CheckListTool.js';
6
9
 
7
10
  class Toolbar {
8
11
  constructor(editor, options) {
9
12
  this.editor = editor;
10
13
  this.options = options;
11
14
  this.toolbar = document.createElement('div');
12
- this.toolbar.className = 'toolbar flex space-x-1.5 p-1.5 bg-stone-100 dark:bg-stone-900 dark:text-stone-200 border-b border-stone-200 dark:border-stone-700';
15
+ this.toolbar.className = 'toolbar flex space-x-1.5 p-1.5 bg-stone-100 dark:bg-stone-900 dark:text-stone-200 border-b border-stone-200 dark:border-stone-700 overflow-x-auto';
13
16
  this.init();
14
17
  }
15
18
 
16
19
  init() {
17
20
  const toolMapping = {
21
+ ul: ULTool,
22
+ ol: OLTool,
23
+ checklist: CheckListTool,
18
24
  bold: BoldTool,
19
25
  italic: ItalicTool,
20
- strikethrough: StrikethroughTool,
21
- preview: PreviewTool
26
+ strikethrough: StrikethroughTool
22
27
  };
23
28
 
29
+ // Append all tools except preview
24
30
  this.options.forEach(tool => {
25
- const ToolClass = toolMapping[tool];
26
- if (ToolClass) {
27
- const toolInstance = new ToolClass(this.editor);
28
- this.toolbar.appendChild(toolInstance.button);
31
+ if (tool !== 'preview') {
32
+ const ToolClass = toolMapping[tool];
33
+ if (ToolClass) {
34
+ const toolInstance = new ToolClass(this.editor);
35
+ this.toolbar.appendChild(toolInstance.button); // Directly append to toolbar
36
+ }
29
37
  }
30
38
  });
31
39
 
40
+ // Append preview button at the end
41
+ if (this.options.includes('preview')) {
42
+ const previewToolInstance = new PreviewTool(this.editor);
43
+ this.toolbar.appendChild(previewToolInstance.button); // Directly append to toolbar at the end
44
+ }
45
+
32
46
  this.editor.editorContainer.insertBefore(this.toolbar, this.editor.markdownEditorDiv);
33
47
  }
34
48
  }
@@ -1,9 +1,9 @@
1
- import MarkdownTool from './MarkdownTool.js';
1
+ import MakeTool from '../MakeTool.js';
2
2
 
3
- class BoldTool extends MarkdownTool {
3
+ class BoldTool extends MakeTool {
4
4
  constructor(editor) {
5
5
  // Call the parent constructor with the markdown syntax for bold (**)
6
- super(editor, '**', 'bold text');
6
+ super(editor, '**', 'Bold');
7
7
  this.button = this.createButton(`
8
8
  <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
9
9
  <path d="M6 12h9a4 4 0 0 1 0 8H7a1 1 0 0 1-1-1V5a1 1 0 0 1 1-1h7a4 4 0 0 1 0 8"/>
@@ -0,0 +1,18 @@
1
+ import MakeTool from '../MakeTool.js';
2
+
3
+ class CheckListTool extends MakeTool {
4
+ constructor(editor) {
5
+ super(editor, '- [x]', 'Check list');
6
+ this.button = this.createButton(`
7
+ <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m3 17 2 2 4-4"/><path d="m3 7 2 2 4-4"/><path d="M13 6h8"/><path d="M13 12h8"/><path d="M13 18h8"/></svg>
8
+ `);
9
+ }
10
+
11
+ // You can change how the syntax is applied for this specific tool:
12
+ applySyntax() {
13
+ super.applySyntax('start'); // Only apply strikethrough at the start
14
+ }
15
+ }
16
+
17
+
18
+ export default CheckListTool;
@@ -1,9 +1,9 @@
1
- import MarkdownTool from './MarkdownTool.js';
1
+ import MakeTool from '../MakeTool.js';
2
2
 
3
- class ItalicTool extends MarkdownTool {
3
+ class ItalicTool extends MakeTool {
4
4
  constructor(editor) {
5
5
  // Call the parent constructor with the markdown syntax for italic (*)
6
- super(editor, '*', 'italic text');
6
+ super(editor, '*', 'Italic');
7
7
  this.button = this.createButton(`
8
8
  <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
9
9
  <line x1="19" y1="4" x2="10" y2="4"/>
@@ -0,0 +1,20 @@
1
+ import MakeTool from '../MakeTool.js';
2
+
3
+ class OLTool extends MakeTool {
4
+ constructor(editor) {
5
+ super(editor, '1.', 'Ordered list');
6
+ this.button = this.createButton(`
7
+ <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
8
+ <path d="M10 12h11"/><path d="M10 18h11"/><path d="M10 6h11"/><path d="M4 10h2"/><path d="M4 6h1v4"/><path d="M6 18H4c0-1 2-2 2-3s-1-1.5-2-1"/>
9
+ </svg>
10
+ `);
11
+ }
12
+
13
+ // You can change how the syntax is applied for this specific tool:
14
+ applySyntax() {
15
+ super.applySyntax('start'); // Only apply strikethrough at the start
16
+ }
17
+ }
18
+
19
+
20
+ export default OLTool;
@@ -1,7 +1,7 @@
1
1
  // #components/Toolbar/tools/PreviewToggleTool.js
2
- import MarkdownTool from './MarkdownTool.js';
2
+ import MakeTool from '../MakeTool.js';
3
3
 
4
- class PreviewTool extends MarkdownTool {
4
+ class PreviewTool extends MakeTool {
5
5
  constructor(editor) {
6
6
  // No markdown syntax for preview toggle, so we call the parent constructor with empty values
7
7
  super(editor, '', 'Preview');
@@ -60,6 +60,12 @@ class PreviewTool extends MarkdownTool {
60
60
  editorDiv.querySelector(".textarea-wrapper").querySelector("textarea").classList.remove("!h-[90lvh]");
61
61
 
62
62
  document.querySelector("body").classList.remove('overflow-hidden');
63
+
64
+ document.querySelectorAll('.markdown-btn').forEach(button => {
65
+ if (!button.classList.contains('preview-btn')) {
66
+ button.classList.remove('pointer-events-none', 'md:pointer-events-auto', 'opacity-25', 'md:opacity-100');
67
+ }
68
+ });
63
69
  }
64
70
 
65
71
  // Method to show the preview (enable it)
@@ -92,6 +98,12 @@ class PreviewTool extends MarkdownTool {
92
98
  editorDiv.querySelector(".textarea-wrapper").querySelector("textarea").classList.add("!h-[90lvh]");
93
99
 
94
100
  document.querySelector("body").classList.add('overflow-hidden');
101
+
102
+ document.querySelectorAll('.markdown-btn').forEach(button => {
103
+ if (!button.classList.contains('preview-btn')) {
104
+ button.classList.add('pointer-events-none', 'md:pointer-events-auto', 'opacity-25', 'md:opacity-100');
105
+ }
106
+ });
95
107
  }
96
108
  }
97
109
 
@@ -1,9 +1,9 @@
1
- import MarkdownTool from './MarkdownTool.js';
1
+ import MakeTool from '../MakeTool.js';
2
2
 
3
- class StrikethroughTool extends MarkdownTool {
3
+ class StrikethroughTool extends MakeTool {
4
4
  constructor(editor) {
5
5
  // Call the parent constructor with the markdown syntax for strikethrough (~~)
6
- super(editor, '~~', 'strikethrough text');
6
+ super(editor, '~~', 'Strikethrough');
7
7
  this.button = this.createButton(`
8
8
  <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
9
9
  <path d="M16 4H9a3 3 0 0 0-2.83 4"/>
@@ -0,0 +1,18 @@
1
+ import MakeTool from '../MakeTool.js';
2
+
3
+ class ULTool extends MakeTool {
4
+ constructor(editor) {
5
+ super(editor, '-', 'Unordered list');
6
+ this.button = this.createButton(`
7
+ <svg xmlns="http://www.w3.org/2000/svg" width="18" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 12h.01"/><path d="M3 18h.01"/><path d="M3 6h.01"/><path d="M8 12h13"/><path d="M8 18h13"/><path d="M8 6h13"/></svg>
8
+ `);
9
+ }
10
+
11
+ // You can change how the syntax is applied for this specific tool:
12
+ applySyntax() {
13
+ super.applySyntax('start'); // Only apply strikethrough at the start
14
+ }
15
+ }
16
+
17
+
18
+ export default ULTool;
@@ -130,9 +130,36 @@ class MarkdownEditor {
130
130
  this.usertextarea.value = `${value.substring(0, selectionStart)}${text}${value.substring(selectionEnd)}`;
131
131
  this.usertextarea.focus();
132
132
  this.usertextarea.setSelectionRange(selectionStart, selectionStart + text.length);
133
+
134
+ // Scroll the textarea to the inserted text
135
+ this.scrollToView();
136
+
133
137
  this.render();
134
138
  }
135
139
 
140
+ scrollToView() {
141
+ const textarea = this.usertextarea;
142
+
143
+ // Calculate the position of the inserted text
144
+ const selectionStart = textarea.selectionStart;
145
+
146
+ // Get the line height (height of each row of text)
147
+ const lineHeight = parseInt(window.getComputedStyle(textarea).lineHeight);
148
+
149
+ // Get how many rows fit into the visible area of the textarea
150
+ const rowsInView = Math.floor(textarea.clientHeight / lineHeight);
151
+
152
+ // Calculate the current line number of the selectionStart
153
+ const currentLine = Math.floor(selectionStart / textarea.cols);
154
+
155
+ // Scroll to the line number that places the inserted text in the center
156
+ const targetScrollTop = (currentLine - Math.floor(rowsInView / 2)) * lineHeight;
157
+
158
+ // Adjust scrollTop to center the cursor's line in the view
159
+ textarea.scrollTop = targetScrollTop;
160
+ }
161
+
162
+
136
163
  render() {
137
164
  const html = marked(this.usertextarea.value);
138
165
  this.previewContent.innerHTML = html;
@@ -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;