diff options
author | Hsieh Chin Fan <pham@topo.tw> | 2024-10-03 08:59:20 +0800 |
---|---|---|
committer | Hsieh Chin Fan <pham@topo.tw> | 2024-10-03 16:19:46 +0800 |
commit | d415e66b355c75d5d1da55f607f9c52cc5a71301 (patch) | |
tree | cc6cd89a6f34fefa485b5bbef816ebc87d9582ed | |
parent | 843be0d63eae1610a2b4a2964ca6b7b48adab478 (diff) |
refactor: set Suggestion as custom element
-rw-r--r-- | src/MenuItem.mjs | 41 | ||||
-rw-r--r-- | src/css/index.css | 5 | ||||
-rw-r--r-- | src/editor.mjs | 22 |
3 files changed, 31 insertions, 37 deletions
diff --git a/src/MenuItem.mjs b/src/MenuItem.mjs index d485032..4028702 100644 --- a/src/MenuItem.mjs +++ b/src/MenuItem.mjs | |||
@@ -134,41 +134,32 @@ export const addGeoLink = ({ utils }, range) => | |||
134 | } | 134 | } |
135 | }) | 135 | }) |
136 | 136 | ||
137 | export class Suggestion { | 137 | export class Suggestion extends Item { |
138 | constructor ({ text, replace }) { | 138 | constructor ({ text, replace, cm }) { |
139 | this.text = text | 139 | super({ text }) |
140 | this.replace = replace | 140 | this.replace = replace |
141 | } | 141 | this.classList.add('suggestion') |
142 | 142 | ||
143 | createElement (codemirror) { | 143 | this.onmouseover = () => { |
144 | const option = document.createElement('div') | 144 | Array.from(this.parentElement?.children)?.forEach(s => |
145 | if (this.text.startsWith('<')) { | ||
146 | option.innerHTML = this.text | ||
147 | } else { | ||
148 | option.innerText = this.text | ||
149 | } | ||
150 | option.classList.add('container__suggestion') | ||
151 | option.onmouseover = () => { | ||
152 | Array.from(option.parentElement?.children)?.forEach(s => | ||
153 | s.classList.remove('focus') | 145 | s.classList.remove('focus') |
154 | ) | 146 | ) |
155 | option.classList.add('focus') | 147 | this.classList.add('focus') |
156 | } | 148 | } |
157 | option.onmouseout = () => { | 149 | this.onmouseout = () => { |
158 | option.classList.remove('focus') | 150 | this.classList.remove('focus') |
159 | } | 151 | } |
160 | option.onclick = () => { | 152 | this.onclick = () => { |
161 | const anchor = codemirror.getCursor() | 153 | const anchor = cm.getCursor() |
162 | codemirror.setSelection(anchor, { ...anchor, ch: 0 }) | 154 | cm.setSelection(anchor, { ...anchor, ch: 0 }) |
163 | codemirror.replaceSelection(this.replace) | 155 | cm.replaceSelection(this.replace) |
164 | codemirror.focus() | 156 | cm.focus() |
165 | const newAnchor = { ...anchor, ch: this.replace.length } | 157 | const newAnchor = { ...anchor, ch: this.replace.length } |
166 | codemirror.setCursor(newAnchor) | 158 | cm.setCursor(newAnchor) |
167 | } | 159 | } |
168 | |||
169 | return option | ||
170 | } | 160 | } |
171 | } | 161 | } |
162 | window.customElements.define('menu-item-suggestion', Suggestion, { extends: 'div' }) | ||
172 | 163 | ||
173 | export const renderResults = ({ modal, modalContent }, map) => | 164 | export const renderResults = ({ modal, modalContent }, map) => |
174 | new Item({ | 165 | new Item({ |
diff --git a/src/css/index.css b/src/css/index.css index 858182d..a2ef454 100644 --- a/src/css/index.css +++ b/src/css/index.css | |||
@@ -117,13 +117,12 @@ body { | |||
117 | } | 117 | } |
118 | } | 118 | } |
119 | 119 | ||
120 | .container__suggestion { | 120 | .suggestion { |
121 | display: flex; | 121 | display: flex; |
122 | overflow: hidden; | 122 | overflow: hidden; |
123 | justify-content: space-between; | 123 | justify-content: space-between; |
124 | align-items: center; | 124 | align-items: center; |
125 | height: fit-content; | 125 | max-width: 700px; |
126 | min-height: 2rem; | ||
127 | 126 | ||
128 | cursor: pointer; | 127 | cursor: pointer; |
129 | white-space: nowrap; | 128 | white-space: nowrap; |
diff --git a/src/editor.mjs b/src/editor.mjs index 2938745..82d93b0 100644 --- a/src/editor.mjs +++ b/src/editor.mjs | |||
@@ -3,6 +3,7 @@ | |||
3 | import { markdown2HTML, generateMaps } from './dumbymap' | 3 | import { markdown2HTML, generateMaps } from './dumbymap' |
4 | import { defaultAliases, parseConfigsFromYaml } from 'mapclay' | 4 | import { defaultAliases, parseConfigsFromYaml } from 'mapclay' |
5 | import * as menuItem from './MenuItem' | 5 | import * as menuItem from './MenuItem' |
6 | import { shiftByWindow } from './utils.mjs' | ||
6 | 7 | ||
7 | // Set up Containers {{{ | 8 | // Set up Containers {{{ |
8 | 9 | ||
@@ -382,7 +383,8 @@ const getSuggestionsForOptions = (optionTyped, validOptions) => { | |||
382 | o => | 383 | o => |
383 | new menuItem.Suggestion({ | 384 | new menuItem.Suggestion({ |
384 | text: `<span>${o.valueOf()}</span><span class='info' title="${o.desc ?? ''}">ⓘ</span>`, | 385 | text: `<span>${o.valueOf()}</span><span class='info' title="${o.desc ?? ''}">ⓘ</span>`, |
385 | replace: `${o.valueOf()}: ` | 386 | replace: `${o.valueOf()}: `, |
387 | cm | ||
386 | }) | 388 | }) |
387 | ) | 389 | ) |
388 | } | 390 | } |
@@ -397,7 +399,8 @@ const getSuggestionFromMapOption = option => { | |||
397 | 399 | ||
398 | return new menuItem.Suggestion({ | 400 | return new menuItem.Suggestion({ |
399 | text, | 401 | text, |
400 | replace: `${option.valueOf()}: ${option.example ?? ''}` | 402 | replace: `${option.valueOf()}: ${option.example ?? ''}`, |
403 | cm | ||
401 | }) | 404 | }) |
402 | } | 405 | } |
403 | // }}} | 406 | // }}} |
@@ -408,7 +411,8 @@ const getSuggestionsFromAliases = option => | |||
408 | const valueString = JSON.stringify(value).replaceAll('"', '') | 411 | const valueString = JSON.stringify(value).replaceAll('"', '') |
409 | return new menuItem.Suggestion({ | 412 | return new menuItem.Suggestion({ |
410 | text: `<span>${alias}</span><span class="truncate" style="color: gray">${valueString}</span>`, | 413 | text: `<span>${alias}</span><span class="truncate" style="color: gray">${valueString}</span>`, |
411 | replace: `${option.valueOf()}: ${valueString}` | 414 | replace: `${option.valueOf()}: ${valueString}`, |
415 | cm | ||
412 | }) | 416 | }) |
413 | }) ?? [] | 417 | }) ?? [] |
414 | // }}} | 418 | // }}} |
@@ -523,7 +527,8 @@ const getSuggestions = anchor => { | |||
523 | ([renderer, info]) => | 527 | ([renderer, info]) => |
524 | new menuItem.Suggestion({ | 528 | new menuItem.Suggestion({ |
525 | text: `<span>use: ${renderer}</span><span class='info' title="${info.desc}">ⓘ</span>`, | 529 | text: `<span>use: ${renderer}</span><span class='info' title="${info.desc}">ⓘ</span>`, |
526 | replace: `use: ${renderer}` | 530 | replace: `use: ${renderer}`, |
531 | cm | ||
527 | }) | 532 | }) |
528 | ) | 533 | ) |
529 | return rendererSuggestions.length > 0 ? rendererSuggestions : [] | 534 | return rendererSuggestions.length > 0 ? rendererSuggestions : [] |
@@ -542,7 +547,6 @@ const addSuggestions = (anchor, suggestions) => { | |||
542 | 547 | ||
543 | menu.innerHTML = '' | 548 | menu.innerHTML = '' |
544 | suggestions | 549 | suggestions |
545 | .map(s => s.createElement(cm)) | ||
546 | .forEach(option => menu.appendChild(option)) | 550 | .forEach(option => menu.appendChild(option)) |
547 | 551 | ||
548 | const widgetAnchor = document.createElement('div') | 552 | const widgetAnchor = document.createElement('div') |
@@ -550,8 +554,8 @@ const addSuggestions = (anchor, suggestions) => { | |||
550 | const rect = widgetAnchor.getBoundingClientRect() | 554 | const rect = widgetAnchor.getBoundingClientRect() |
551 | menu.style.left = `calc(${rect.left}px + 2rem)` | 555 | menu.style.left = `calc(${rect.left}px + 2rem)` |
552 | menu.style.top = `calc(${rect.bottom}px + 1rem)` | 556 | menu.style.top = `calc(${rect.bottom}px + 1rem)` |
553 | menu.style.maxWidth = `calc(${window.innerWidth}px - ${rect.x}px - 3rem)` | ||
554 | menu.style.display = 'block' | 557 | menu.style.display = 'block' |
558 | shiftByWindow(menu) | ||
555 | } | 559 | } |
556 | // }}} | 560 | // }}} |
557 | // EVENT: Suggests for current selection {{{ | 561 | // EVENT: Suggests for current selection {{{ |
@@ -583,7 +587,7 @@ cm.on('keydown', (_, e) => { | |||
583 | ) { return } | 587 | ) { return } |
584 | 588 | ||
585 | // Directly add a newline when no suggestion is selected | 589 | // Directly add a newline when no suggestion is selected |
586 | const currentSuggestion = menu.querySelector('.container__suggestion.focus') | 590 | const currentSuggestion = menu.querySelector('.menu-item.focus') |
587 | if (!currentSuggestion && e.key === 'Enter') return | 591 | if (!currentSuggestion && e.key === 'Enter') return |
588 | 592 | ||
589 | // Override default behavior | 593 | // Override default behavior |
@@ -592,10 +596,10 @@ cm.on('keydown', (_, e) => { | |||
592 | // Suggestion when pressing Tab or Shift + Tab | 596 | // Suggestion when pressing Tab or Shift + Tab |
593 | const nextSuggestion = | 597 | const nextSuggestion = |
594 | currentSuggestion?.nextSibling ?? | 598 | currentSuggestion?.nextSibling ?? |
595 | menu.querySelector('.container__suggestion:first-child') | 599 | menu.querySelector('.menu-item:first-child') |
596 | const previousSuggestion = | 600 | const previousSuggestion = |
597 | currentSuggestion?.previousSibling ?? | 601 | currentSuggestion?.previousSibling ?? |
598 | menu.querySelector('.container__suggestion:last-child') | 602 | menu.querySelector('.menu-item:last-child') |
599 | const focusSuggestion = e.shiftKey ? previousSuggestion : nextSuggestion | 603 | const focusSuggestion = e.shiftKey ? previousSuggestion : nextSuggestion |
600 | 604 | ||
601 | // Current editor selection state | 605 | // Current editor selection state |