aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/editor.mjs
diff options
context:
space:
mode:
authorHsieh Chin Fan <pham@topo.tw>2024-09-24 20:07:19 +0800
committerHsieh Chin Fan <pham@topo.tw>2024-09-25 02:05:56 +0800
commit54f0c9381fce41c4ca46baebfd6281a8f9f9ff93 (patch)
tree4d81f07eeebf22a535d1e673d94856767910f724 /src/editor.mjs
parent34587b0d17cddaf5d5a11ffda4019acda9bc0864 (diff)
refactor: general menu
* rename suggestionEle -> menu for general use cases * set Suggestion as module
Diffstat (limited to 'src/editor.mjs')
-rw-r--r--src/editor.mjs73
1 files changed, 23 insertions, 50 deletions
diff --git a/src/editor.mjs b/src/editor.mjs
index 9ff8be6..92513a3 100644
--- a/src/editor.mjs
+++ b/src/editor.mjs
@@ -1,8 +1,8 @@
1/*global EasyMDE*/ 1/*global EasyMDE*/
2/*eslint no-undef: "error"*/ 2/*eslint no-undef: "error"*/
3import { markdown2HTML, generateMaps } from './dumbymap' 3import { markdown2HTML, generateMaps, createDocLinks } from './dumbymap'
4import { defaultAliases, parseConfigsFromYaml } from 'mapclay' 4import { defaultAliases, parseConfigsFromYaml } from 'mapclay'
5import { createDocLinks } from './dumbymap.mjs' 5import { Suggestion } from './MenuItem'
6 6
7// Set up Containers {{{ 7// Set up Containers {{{
8 8
@@ -214,18 +214,13 @@ window.onhashchange = () => {
214// }}} 214// }}}
215// Completion in Code Blok {{{ 215// Completion in Code Blok {{{
216// Elements about suggestions {{{ 216// Elements about suggestions {{{
217const suggestionsEle = document.createElement('div') 217const menu = document.createElement('div')
218suggestionsEle.classList.add('container__suggestions'); 218menu.id = 'menu'
219document.body.append(suggestionsEle) 219menu.onclick = () => menu.style.display = 'none'
220document.body.append(menu)
220 221
221const rendererOptions = {} 222const rendererOptions = {}
222 223
223class Suggestion {
224 constructor({ text, replace }) {
225 this.text = text
226 this.replace = replace
227 }
228}
229// }}} 224// }}}
230// Aliases for map options {{{ 225// Aliases for map options {{{
231const aliasesForMapOptions = {} 226const aliasesForMapOptions = {}
@@ -460,51 +455,29 @@ const getSuggestions = (anchor) => {
460const addSuggestions = (anchor, suggestions) => { 455const addSuggestions = (anchor, suggestions) => {
461 456
462 if (suggestions.length === 0) { 457 if (suggestions.length === 0) {
463 suggestionsEle.style.display = 'none'; 458 menu.style.display = 'none';
464 return 459 return
465 } else { 460 } else {
466 suggestionsEle.style.display = 'block'; 461 menu.style.display = 'block';
467 } 462 }
468 463
469 suggestionsEle.innerHTML = '' 464 menu.innerHTML = ''
470 suggestions.forEach((suggestion) => { 465 suggestions
471 const option = document.createElement('div'); 466 .map(s => s.createElement(cm))
472 if (suggestion.text.startsWith('<')) { 467 .forEach(option => menu.appendChild(option))
473 option.innerHTML = suggestion.text;
474 } else {
475 option.innerText = suggestion.text;
476 }
477 option.classList.add('container__suggestion');
478 option.onmouseover = () => {
479 Array.from(suggestionsEle.children).forEach(s => s.classList.remove('focus'))
480 option.classList.add('focus')
481 }
482 option.onmouseout = () => {
483 option.classList.remove('focus')
484 }
485 option.onclick = () => {
486 cm.setSelection(anchor, { ...anchor, ch: 0 })
487 cm.replaceSelection(suggestion.replace)
488 cm.focus();
489 const newAnchor = { ...anchor, ch: suggestion.replace.length }
490 cm.setCursor(newAnchor);
491 };
492 suggestionsEle.appendChild(option);
493 });
494 468
495 const widgetAnchor = document.createElement('div') 469 const widgetAnchor = document.createElement('div')
496 cm.addWidget(anchor, widgetAnchor, true) 470 cm.addWidget(anchor, widgetAnchor, true)
497 const rect = widgetAnchor.getBoundingClientRect() 471 const rect = widgetAnchor.getBoundingClientRect()
498 suggestionsEle.style.left = `calc(${rect.left}px + 2rem)`; 472 menu.style.left = `calc(${rect.left}px + 2rem)`;
499 suggestionsEle.style.top = `calc(${rect.bottom}px + 1rem)`; 473 menu.style.top = `calc(${rect.bottom}px + 1rem)`;
500 suggestionsEle.style.maxWidth = `calc(${window.innerWidth}px - ${rect.x}px - 3rem)`; 474 menu.style.maxWidth = `calc(${window.innerWidth}px - ${rect.x}px - 3rem)`;
501 suggestionsEle.style.display = 'block' 475 menu.style.display = 'block'
502} 476}
503// }}} 477// }}}
504// EVENT: Suggests for current selection {{{ 478// EVENT: Suggests for current selection {{{
505// FIXME Dont show suggestion when selecting multiple chars 479// FIXME Dont show suggestion when selecting multiple chars
506cm.on("cursorActivity", (_) => { 480cm.on("cursorActivity", (_) => {
507 suggestionsEle.style.display = 'none'
508 const anchor = cm.getCursor() 481 const anchor = cm.getCursor()
509 482
510 if (insideCodeblockForMap(anchor)) { 483 if (insideCodeblockForMap(anchor)) {
@@ -512,7 +485,7 @@ cm.on("cursorActivity", (_) => {
512 } 485 }
513}); 486});
514cm.on("blur", () => { 487cm.on("blur", () => {
515 suggestionsEle.style.display = 'none' 488 menu.style.display = 'none'
516 cm.getWrapperElement().classList.remove('focus') 489 cm.getWrapperElement().classList.remove('focus')
517 HtmlContainer.classList.add('focus') 490 HtmlContainer.classList.add('focus')
518}) 491})
@@ -520,25 +493,25 @@ cm.on("blur", () => {
520// EVENT: keydown for suggestions {{{ 493// EVENT: keydown for suggestions {{{
521const keyForSuggestions = ['Tab', 'Enter', 'Escape'] 494const keyForSuggestions = ['Tab', 'Enter', 'Escape']
522cm.on('keydown', (_, e) => { 495cm.on('keydown', (_, e) => {
523 if (!cm.hasFocus || !keyForSuggestions.includes(e.key) || suggestionsEle.style.display === 'none') return; 496 if (!cm.hasFocus || !keyForSuggestions.includes(e.key) || menu.style.display === 'none') return;
524 497
525 // Directly add a newline when no suggestion is selected 498 // Directly add a newline when no suggestion is selected
526 const currentSuggestion = suggestionsEle.querySelector('.container__suggestion.focus') 499 const currentSuggestion = menu.querySelector('.container__suggestion.focus')
527 if (!currentSuggestion && e.key === 'Enter') return 500 if (!currentSuggestion && e.key === 'Enter') return
528 501
529 // Override default behavior 502 // Override default behavior
530 e.preventDefault(); 503 e.preventDefault();
531 504
532 // Suggestion when pressing Tab or Shift + Tab 505 // Suggestion when pressing Tab or Shift + Tab
533 const nextSuggestion = currentSuggestion?.nextSibling ?? suggestionsEle.querySelector('.container__suggestion:first-child') 506 const nextSuggestion = currentSuggestion?.nextSibling ?? menu.querySelector('.container__suggestion:first-child')
534 const previousSuggestion = currentSuggestion?.previousSibling ?? suggestionsEle.querySelector('.container__suggestion:last-child') 507 const previousSuggestion = currentSuggestion?.previousSibling ?? menu.querySelector('.container__suggestion:last-child')
535 const focusSuggestion = e.shiftKey ? previousSuggestion : nextSuggestion 508 const focusSuggestion = e.shiftKey ? previousSuggestion : nextSuggestion
536 509
537 // Current editor selection state 510 // Current editor selection state
538 const anchor = cm.getCursor() 511 const anchor = cm.getCursor()
539 switch (e.key) { 512 switch (e.key) {
540 case 'Tab': 513 case 'Tab':
541 Array.from(suggestionsEle.children).forEach(s => s.classList.remove('focus')) 514 Array.from(menu.children).forEach(s => s.classList.remove('focus'))
542 focusSuggestion.classList.add('focus') 515 focusSuggestion.classList.add('focus')
543 focusSuggestion.scrollIntoView({ behavior: 'smooth', block: 'nearest' }) 516 focusSuggestion.scrollIntoView({ behavior: 'smooth', block: 'nearest' })
544 break; 517 break;
@@ -546,7 +519,7 @@ cm.on('keydown', (_, e) => {
546 currentSuggestion.onclick() 519 currentSuggestion.onclick()
547 break; 520 break;
548 case 'Escape': 521 case 'Escape':
549 suggestionsEle.style.display = 'none'; 522 menu.style.display = 'none';
550 // Focus editor again 523 // Focus editor again
551 setTimeout(() => cm.focus() && cm.setCursor(anchor), 100) 524 setTimeout(() => cm.focus() && cm.setCursor(anchor), 100)
552 break; 525 break;