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 /src | |
| parent | 843be0d63eae1610a2b4a2964ca6b7b48adab478 (diff) | |
refactor: set Suggestion as custom element
Diffstat (limited to 'src')
| -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 |