Mercurial
comparison rich_editor/rich_editor.js @ 204:e5aed6c36672
[Notes] Added icons and updated styling a bit. Probalby usable now.
| author | MrJuneJune <me@mrjunejune.com> |
|---|---|
| date | Sun, 15 Feb 2026 11:02:13 -0800 |
| parents | 6cdee35a7ba9 |
| children | 240337164a80 |
comparison
equal
deleted
inserted
replaced
| 203:92a57bd716c1 | 204:e5aed6c36672 |
|---|---|
| 42 function createToolbar(editor) { | 42 function createToolbar(editor) { |
| 43 const toolbar = document.createElement('div'); | 43 const toolbar = document.createElement('div'); |
| 44 toolbar.className = 'rich-editor-toolbar'; | 44 toolbar.className = 'rich-editor-toolbar'; |
| 45 | 45 |
| 46 const buttons = [ | 46 const buttons = [ |
| 47 { cmd: 'h1', label: 'H1', title: 'Heading 1' }, | 47 { cmd: 'h1', label: '', title: 'Heading 1', icons: "/public/icons/h1.png" }, |
| 48 { cmd: 'h2', label: 'H2', title: 'Heading 2' }, | 48 { cmd: 'h2', label: '', title: 'Heading 2', icons: "/public/icons/h2.png" }, |
| 49 { cmd: 'h3', label: 'H3', title: 'Heading 3' }, | 49 { cmd: 'h3', label: '', title: 'Heading 3', icons: "/public/icons/h3.png" }, |
| 50 { cmd: 'p', label: 'ΒΆ', title: 'Paragraph' }, | 50 { cmd: 'p', label: 'ΒΆ', title: 'Paragraph' }, |
| 51 { cmd: 'ul', label: 'β’', title: 'Bullet List' }, | 51 { cmd: 'ul', label: 'β’', title: 'Bullet List' }, |
| 52 { cmd: 'ol', label: '1.', title: 'Numbered List' }, | 52 { cmd: 'ol', label: '1.', title: 'Numbered List' }, |
| 53 { cmd: 'bold', label: '', title: 'Bold', icons: "/public/icons/bold.png" }, | |
| 53 { cmd: 'separator' }, | 54 { cmd: 'separator' }, |
| 54 { cmd: 'bold', label: 'B', title: 'Bold' }, | 55 { cmd: 'link', label: '', title: 'Insert Link', icons: "/public/icons/link.png" }, |
| 55 { cmd: 'italic', label: 'I', title: 'Italic' }, | 56 { cmd: 'notelink', label: '', title: 'Link to Note', icons: "/public/icons/binder.png" }, |
| 57 { cmd: 'upload', label: '', title: 'Upload File', icons: "/public/icons/clipy.png" }, | |
| 56 { cmd: 'separator' }, | 58 { cmd: 'separator' }, |
| 57 { cmd: 'link', label: 'π', title: 'Insert Link' }, | 59 { cmd: 'readonly', label: '', title: 'readonly', icons: "/public/icons/book.png" }, |
| 58 { cmd: 'notelink', label: 'π', title: 'Link to Note' }, | |
| 59 { cmd: 'upload', label: 'π', title: 'Upload File' } | |
| 60 ]; | 60 ]; |
| 61 | 61 |
| 62 buttons.forEach(btn => { | 62 buttons.forEach(btn => { |
| 63 if (btn.cmd === 'separator') { | 63 if (btn.cmd === 'separator') { |
| 64 const sep = document.createElement('span'); | 64 const sep = document.createElement('span'); |
| 68 } | 68 } |
| 69 | 69 |
| 70 const button = document.createElement('button'); | 70 const button = document.createElement('button'); |
| 71 button.type = 'button'; | 71 button.type = 'button'; |
| 72 button.className = 'rich-editor-btn'; | 72 button.className = 'rich-editor-btn'; |
| 73 if (btn.icons) | |
| 74 button.style.backgroundImage = `url("${btn.icons}")`; | |
| 73 button.textContent = btn.label; | 75 button.textContent = btn.label; |
| 74 button.title = btn.title; | 76 button.title = btn.title; |
| 75 button.dataset.cmd = btn.cmd; | 77 button.dataset.cmd = btn.cmd; |
| 76 | 78 |
| 77 button.addEventListener('click', (e) => { | 79 button.addEventListener('click', (e) => { |
| 112 border-radius: 4px; | 114 border-radius: 4px; |
| 113 background: #fff; | 115 background: #fff; |
| 114 cursor: pointer; | 116 cursor: pointer; |
| 115 font-size: 14px; | 117 font-size: 14px; |
| 116 min-width: 32px; | 118 min-width: 32px; |
| 119 background-repeat: no-repeat; | |
| 120 background-position: center; | |
| 121 background-size: 24px 24px; | |
| 122 border: none; | |
| 117 } | 123 } |
| 118 | 124 |
| 119 .rich-editor-btn:hover { | 125 .rich-editor-btn:hover { |
| 120 background: #e9e9e9; | 126 background: #e9e9e9; |
| 127 background-repeat: no-repeat; | |
| 128 background-position: center; | |
| 129 background-size: 24px 24px; | |
| 121 } | 130 } |
| 122 | 131 |
| 123 .rich-editor-btn:active { | 132 .rich-editor-btn:active { |
| 124 background: #ddd; | 133 background: #ddd; |
| 134 background-repeat: no-repeat; | |
| 135 background-position: center; | |
| 136 background-size: 24px 24px; | |
| 125 } | 137 } |
| 126 | 138 |
| 127 .rich-editor-separator { | 139 .rich-editor-separator { |
| 128 width: 1px; | 140 width: 1px; |
| 129 background: #ccc; | 141 background: #ccc; |
| 216 } | 228 } |
| 217 | 229 |
| 218 .rich-editor-file-input { | 230 .rich-editor-file-input { |
| 219 display: none; | 231 display: none; |
| 220 } | 232 } |
| 233 | |
| 234 ol,ul { | |
| 235 padding: 16px; | |
| 236 } | |
| 221 `; | 237 `; |
| 222 document.head.appendChild(style); | 238 document.head.appendChild(style); |
| 223 } | 239 } |
| 224 | 240 |
| 225 class Editor { | 241 class Editor { |
| 226 constructor(elementId, options) { | 242 constructor(elementId, options) { |
| 227 this.options = { ...DEFAULT_OPTIONS, ...options }; | 243 this.options = { ...DEFAULT_OPTIONS, ...options }; |
| 228 this.container = document.getElementById(elementId); | 244 this.container = document.getElementById(elementId); |
| 245 this.state = { readOnly: false }; | |
| 229 | 246 |
| 230 if (!this.container) { | 247 if (!this.container) { |
| 231 throw new Error(`Element with id "${elementId}" not found`); | 248 throw new Error(`Element with id "${elementId}" not found`); |
| 232 } | 249 } |
| 233 | 250 |
| 394 | 411 |
| 395 triggerFileUpload() { | 412 triggerFileUpload() { |
| 396 this.fileInput.click(); | 413 this.fileInput.click(); |
| 397 } | 414 } |
| 398 | 415 |
| 416 readOnly() { | |
| 417 this.state.readOnly = !this.state.readOnly; | |
| 418 | |
| 419 if (this.state.readOnly) | |
| 420 { | |
| 421 this.content.contentEditable = false; | |
| 422 this.setStatus('Read-only mode'); | |
| 423 const buttons = this.toolbar.querySelectorAll('.rich-editor-btn'); | |
| 424 buttons.forEach(btn => { | |
| 425 if (btn.dataset.cmd !== 'readonly') { | |
| 426 btn.disabled = true; | |
| 427 } | |
| 428 }); | |
| 429 } | |
| 430 else | |
| 431 { | |
| 432 // Enable editing | |
| 433 this.content.contentEditable = true; | |
| 434 this.content.style.backgroundColor = ''; | |
| 435 this.setStatus('Ready'); | |
| 436 | |
| 437 // Enable toolbar buttons | |
| 438 const buttons = this.toolbar.querySelectorAll('.rich-editor-btn'); | |
| 439 buttons.forEach(btn => { | |
| 440 btn.disabled = false; | |
| 441 }); | |
| 442 } | |
| 443 } | |
| 444 | |
| 399 async uploadFile(file) { | 445 async uploadFile(file) { |
| 400 if (!this.options.uploadCallback) { | 446 if (!this.options.uploadCallback) { |
| 401 console.warn('No upload callback configured'); | 447 console.warn('No upload callback configured'); |
| 402 return; | 448 return; |
| 403 } | 449 } |
| 496 this.insertNoteLink(); | 542 this.insertNoteLink(); |
| 497 break; | 543 break; |
| 498 case 'upload': | 544 case 'upload': |
| 499 this.triggerFileUpload(); | 545 this.triggerFileUpload(); |
| 500 break; | 546 break; |
| 547 case 'readonly': | |
| 548 this.readOnly(); | |
| 549 break; | |
| 501 } | 550 } |
| 502 | 551 |
| 503 if (this.debouncedSave) this.debouncedSave(); | 552 if (this.debouncedSave) this.debouncedSave(); |
| 504 } | 553 } |
| 505 | 554 |