diff options
| -rw-r--r-- | src/css/dumbymap.css | 27 | ||||
| -rw-r--r-- | src/dumbymap.mjs | 282 | ||||
| -rw-r--r-- | src/editor.mjs | 32 |
3 files changed, 208 insertions, 133 deletions
diff --git a/src/css/dumbymap.css b/src/css/dumbymap.css index 6554ceb..03c6c2a 100644 --- a/src/css/dumbymap.css +++ b/src/css/dumbymap.css | |||
| @@ -40,8 +40,11 @@ | |||
| 40 | 40 | ||
| 41 | .Showcase { | 41 | .Showcase { |
| 42 | order: 2; | 42 | order: 2; |
| 43 | #mapPlaceholder { | 43 | display: none; |
| 44 | display: none; | 44 | |
| 45 | .map-container { | ||
| 46 | width: 100% !important; | ||
| 47 | height: 100% !important; | ||
| 45 | } | 48 | } |
| 46 | } | 49 | } |
| 47 | 50 | ||
| @@ -74,9 +77,11 @@ | |||
| 74 | } | 77 | } |
| 75 | 78 | ||
| 76 | .map-container { | 79 | .map-container { |
| 80 | background: white; | ||
| 77 | margin: 2px; | 81 | margin: 2px; |
| 78 | &.focus { | 82 | border: 3px solid transparent; |
| 79 | border: solid gray; | 83 | &[data-focus=true] { |
| 84 | border: 3px solid gray; | ||
| 80 | } | 85 | } |
| 81 | } | 86 | } |
| 82 | 87 | ||
| @@ -90,6 +95,11 @@ | |||
| 90 | width: fit-content; | 95 | width: fit-content; |
| 91 | flex: 0 0; | 96 | flex: 0 0; |
| 92 | } | 97 | } |
| 98 | [data-placeholder] { | ||
| 99 | box-sizing: border-box; | ||
| 100 | border: 5px solid white; | ||
| 101 | background: lightgray; | ||
| 102 | margin: 0; | ||
| 93 | } | 103 | } |
| 94 | } | 104 | } |
| 95 | 105 | ||
| @@ -116,6 +126,10 @@ | |||
| 116 | display: none; | 126 | display: none; |
| 117 | pointer-events: none; | 127 | pointer-events: none; |
| 118 | } | 128 | } |
| 129 | |||
| 130 | .Showcase { | ||
| 131 | display: block; | ||
| 132 | } | ||
| 119 | } | 133 | } |
| 120 | 134 | ||
| 121 | .DumbyMap[data-layout=side] { | 135 | .DumbyMap[data-layout=side] { |
| @@ -191,9 +205,14 @@ | |||
| 191 | } | 205 | } |
| 192 | } | 206 | } |
| 193 | } | 207 | } |
| 208 | |||
| 194 | > :not(.draggable-block) { | 209 | > :not(.draggable-block) { |
| 195 | display: none; | 210 | display: none; |
| 196 | } | 211 | } |
| 212 | |||
| 213 | [data-placeholder] { | ||
| 214 | max-width: 50px; | ||
| 215 | } | ||
| 197 | } | 216 | } |
| 198 | } | 217 | } |
| 199 | 218 | ||
diff --git a/src/dumbymap.mjs b/src/dumbymap.mjs index 6b64ee4..5800351 100644 --- a/src/dumbymap.mjs +++ b/src/dumbymap.mjs | |||
| @@ -217,90 +217,6 @@ export const generateMaps = async (container, callback) => { | |||
| 217 | childRect.bottom < parentRect.bottom - offset | 217 | childRect.bottom < parentRect.bottom - offset |
| 218 | } | 218 | } |
| 219 | //}}} | 219 | //}}} |
| 220 | // Render Maps {{{ | ||
| 221 | |||
| 222 | const afterEachMapLoaded = (mapContainer) => { | ||
| 223 | const focusClickedMap = () => { | ||
| 224 | if (container.getAttribute('data-layout') !== 'none') return | ||
| 225 | |||
| 226 | container.querySelectorAll('.map-container') | ||
| 227 | .forEach(c => c.classList.remove('focus')) | ||
| 228 | mapContainer.classList.add('focus') | ||
| 229 | } | ||
| 230 | mapContainer.onclick = focusClickedMap | ||
| 231 | } | ||
| 232 | |||
| 233 | // Set unique ID for map container | ||
| 234 | const mapIdList = [] | ||
| 235 | const assignMapId = (config) => { | ||
| 236 | let mapId = config.id | ||
| 237 | if (!mapId) { | ||
| 238 | mapId = config.use?.split('/')?.at(-1) | ||
| 239 | let counter = 2 | ||
| 240 | while (mapIdList.includes(mapId)) { | ||
| 241 | mapId = `${config.use}.${counter}` | ||
| 242 | counter++ | ||
| 243 | } | ||
| 244 | config.id = mapId | ||
| 245 | } | ||
| 246 | mapIdList.push(mapId) | ||
| 247 | return config | ||
| 248 | } | ||
| 249 | |||
| 250 | // Render each code block with "language-map" class | ||
| 251 | const render = renderWith(config => ({ width: "100%", ...config })) | ||
| 252 | const renderTargets = Array.from(container.querySelectorAll('pre:has(.language-map)')) | ||
| 253 | .map(async (target) => { | ||
| 254 | // Get text in code block starts with '```map' | ||
| 255 | const configText = target.querySelector('.language-map') | ||
| 256 | .textContent | ||
| 257 | // BE CAREFUL!!! 0xa0 char is "non-breaking spaces" in HTML text content | ||
| 258 | // replace it by normal space | ||
| 259 | .replace(/\u00A0/g, '\u0020') | ||
| 260 | |||
| 261 | let configList = [] | ||
| 262 | try { | ||
| 263 | configList = parseConfigsFromYaml(configText).map(assignMapId) | ||
| 264 | } catch (_) { | ||
| 265 | console.warn('Fail to parse yaml config for element', target) | ||
| 266 | } | ||
| 267 | |||
| 268 | // Render maps | ||
| 269 | return render(target, configList) | ||
| 270 | .then(results => { | ||
| 271 | results.forEach((mapByConfig) => { | ||
| 272 | if (mapByConfig.status === 'fulfilled') { | ||
| 273 | afterEachMapLoaded(mapByConfig.value) | ||
| 274 | return mapByConfig.value | ||
| 275 | } else { | ||
| 276 | console.error('Fail to render target element', mapByConfig.reason) | ||
| 277 | } | ||
| 278 | }) | ||
| 279 | }) | ||
| 280 | }) | ||
| 281 | |||
| 282 | const renderAllTargets = Promise.all(renderTargets) | ||
| 283 | renderAllTargets.then(() => { | ||
| 284 | console.info('Finish Rendering') | ||
| 285 | |||
| 286 | const maps = htmlHolder.querySelectorAll('.map-container') ?? [] | ||
| 287 | Array.from(maps) | ||
| 288 | .forEach(ele => { | ||
| 289 | callback(ele) | ||
| 290 | const markers = geoLinks | ||
| 291 | .filter(link => !link.targets || link.targets.includes(ele.id)) | ||
| 292 | .map(link => ({ | ||
| 293 | xy: link.xy, | ||
| 294 | title: link.url.pathname | ||
| 295 | })) | ||
| 296 | ele?.renderer?.addMarkers(markers) | ||
| 297 | }) | ||
| 298 | |||
| 299 | htmlHolder.querySelectorAll('.marker') | ||
| 300 | .forEach(marker => htmlHolder.anchors.push(marker)) | ||
| 301 | }) | ||
| 302 | |||
| 303 | //}}} | ||
| 304 | // Draggable Blocks{{{ | 220 | // Draggable Blocks{{{ |
| 305 | // Add draggable part for blocks | 221 | // Add draggable part for blocks |
| 306 | htmlHolder.blocks = Array.from(htmlHolder.querySelectorAll('.draggable-block')) | 222 | htmlHolder.blocks = Array.from(htmlHolder.querySelectorAll('.draggable-block')) |
| @@ -322,16 +238,35 @@ export const generateMaps = async (container, callback) => { | |||
| 322 | 238 | ||
| 323 | // }}} | 239 | // }}} |
| 324 | // CSS observer {{{ | 240 | // CSS observer {{{ |
| 325 | |||
| 326 | // Set focusArea | 241 | // Set focusArea |
| 327 | const showcase = document.createElement('div') | 242 | const showcase = document.createElement('div') |
| 328 | container.appendChild(showcase) | 243 | container.appendChild(showcase) |
| 329 | showcase.classList.add('Showcase') | 244 | showcase.classList.add('Showcase') |
| 330 | const mapPlaceholder = document.createElement('div') | ||
| 331 | mapPlaceholder.id = 'mapPlaceholder' | ||
| 332 | showcase.appendChild(mapPlaceholder) | ||
| 333 | 245 | ||
| 334 | // Layout{{{ | 246 | // Focus Map {{{ |
| 247 | const mapFocusObserver = () => new MutationObserver((mutations) => { | ||
| 248 | const mutation = mutations.at(-1) | ||
| 249 | const target = mutation.target | ||
| 250 | const focus = target.getAttribute(mutation.attributeName) === 'true' | ||
| 251 | const shouldBeInShowcase = focus && getComputedStyle(showcase).display !== 'none' | ||
| 252 | |||
| 253 | if (shouldBeInShowcase) { | ||
| 254 | if (showcase.contains(target)) return | ||
| 255 | |||
| 256 | const placeholder = document.createElement('div') | ||
| 257 | placeholder.setAttribute('data-placeholder', target.id) | ||
| 258 | placeholder.style.width = target.style.width | ||
| 259 | target.parentElement.replaceChild(placeholder, target) | ||
| 260 | showcase.appendChild(target) | ||
| 261 | } else if (showcase.contains(target)) { | ||
| 262 | const placeholder = htmlHolder.querySelector(`[data-placeholder="${target.id}"]`) | ||
| 263 | if (!placeholder) throw Error(`Cannot fine placeholder for map "${target.id}"`) | ||
| 264 | placeholder.parentElement.replaceChild(target, placeholder) | ||
| 265 | placeholder.remove() | ||
| 266 | } | ||
| 267 | }) | ||
| 268 | // }}} | ||
| 269 | // Layout {{{ | ||
| 335 | 270 | ||
| 336 | // press key to switch layout | 271 | // press key to switch layout |
| 337 | const layouts = ['none', 'side', 'overlay'] | 272 | const layouts = ['none', 'side', 'overlay'] |
| @@ -339,41 +274,62 @@ export const generateMaps = async (container, callback) => { | |||
| 339 | 274 | ||
| 340 | // FIXME Use UI to switch layouts | 275 | // FIXME Use UI to switch layouts |
| 341 | const originalKeyDown = document.onkeydown | 276 | const originalKeyDown = document.onkeydown |
| 342 | document.onkeydown = (event) => { | 277 | document.onkeydown = (e) => { |
| 343 | originalKeyDown(event) | 278 | const event = originalKeyDown(e) |
| 279 | if (!event) return | ||
| 280 | |||
| 344 | if (event.key === 'x' && container.querySelector('.map-container')) { | 281 | if (event.key === 'x' && container.querySelector('.map-container')) { |
| 282 | e.preventDefault() | ||
| 345 | let currentLayout = container.getAttribute('data-layout') | 283 | let currentLayout = container.getAttribute('data-layout') |
| 346 | currentLayout = currentLayout ? currentLayout : 'none' | 284 | currentLayout = currentLayout ? currentLayout : 'none' |
| 347 | const nextLayout = layouts[(layouts.indexOf(currentLayout) + 1) % layouts.length] | 285 | const nextIndex = (layouts.indexOf(currentLayout) + 1) % layouts.length |
| 286 | const nextLayout = layouts[nextIndex] | ||
| 348 | 287 | ||
| 349 | container.setAttribute("data-layout", nextLayout) | 288 | container.setAttribute("data-layout", nextLayout) |
| 350 | } | 289 | } |
| 290 | |||
| 291 | if (event.key === 'Tab') { | ||
| 292 | e.preventDefault() | ||
| 293 | |||
| 294 | const selector = '.map-container, [data-placeholder]' | ||
| 295 | const candidates = Array.from(htmlHolder.querySelectorAll(selector)) | ||
| 296 | if (candidates.length === 0) return | ||
| 297 | |||
| 298 | const currentFocus = htmlHolder.querySelector('.map-container[data-focus=true]') | ||
| 299 | ?? htmlHolder.querySelector('[data-placeholder]') | ||
| 300 | Array.from(container.querySelectorAll('.map-container')).forEach(e => | ||
| 301 | e.removeAttribute('data-focus') | ||
| 302 | ); | ||
| 303 | const index = currentFocus | ||
| 304 | ? (candidates.indexOf(currentFocus) + (event.shiftKey ? -1 : 1)) % candidates.length | ||
| 305 | : 0 | ||
| 306 | const nextFocus = candidates.at(index) | ||
| 307 | nextFocus.setAttribute('data-focus', "true") | ||
| 308 | } | ||
| 351 | } | 309 | } |
| 352 | 310 | ||
| 353 | // observe layout change | 311 | // observe layout change |
| 354 | const layoutObserver = new MutationObserver(() => { | 312 | const layoutObserver = new MutationObserver((mutations) => { |
| 355 | const layout = container.getAttribute('data-layout') | 313 | const mutation = mutations.at(-1) |
| 356 | htmlHolder.blocks.forEach(b => b.style.display = "block") | 314 | const layout = container.getAttribute(mutation.attributeName) |
| 357 | 315 | ||
| 358 | if (layout === 'none') { | 316 | // Trigger Mutation Observer |
| 359 | mapPlaceholder.innerHTML = "" | 317 | const focusMap = container.querySelector('.map-container[data-focus=true]') |
| 360 | const map = showcase.querySelector('.map-container') | 318 | ?? container.querySelector('.map-container') |
| 361 | // Swap focused map and palceholder in markdown | 319 | focusMap?.setAttribute('data-focus', 'true') |
| 362 | if (map) { | 320 | |
| 363 | mapPlaceholder.parentElement?.replaceChild(map, mapPlaceholder) | 321 | // Check empty block with map-container in showcase |
| 364 | showcase.append(mapPlaceholder) | 322 | htmlHolder.blocks.forEach(b => { |
| 365 | } | 323 | const contentChildren = Array.from(b.querySelectorAll(':scope > :not(.draggable)')) ?? [] |
| 366 | } else { | 324 | if (contentChildren.length === 1 |
| 367 | // If paceholder is not set, create one and put map into focusArea | 325 | && elementsWithMapConfig.includes(contentChildren[0]) |
| 368 | if (showcase.contains(mapPlaceholder)) { | 326 | && !contentChildren[0].querySelector('.map-container') |
| 369 | const mapContainer = container.querySelector('.map-container.focus') ?? container.querySelector('.map-container') | 327 | ) { |
| 370 | mapPlaceholder.innerHTML = `<div>Placeholder</div>` | 328 | b.style.display = "none" |
| 371 | // TODO Get snapshot image | 329 | } else { |
| 372 | // mapPlaceholder.src = map.map.getCanvas().toDataURL() | 330 | b.style.display = "block" |
| 373 | mapContainer.parentElement?.replaceChild(mapPlaceholder, mapContainer) | ||
| 374 | showcase.appendChild(mapContainer) | ||
| 375 | } | 331 | } |
| 376 | } | 332 | }) |
| 377 | 333 | ||
| 378 | if (layout === 'overlay') { | 334 | if (layout === 'overlay') { |
| 379 | let [x, y] = [0, 0]; | 335 | let [x, y] = [0, 0]; |
| @@ -398,7 +354,9 @@ export const generateMaps = async (container, callback) => { | |||
| 398 | block.removeAttribute('style') | 354 | block.removeAttribute('style') |
| 399 | try { | 355 | try { |
| 400 | block.draggableInstance.remove() | 356 | block.draggableInstance.remove() |
| 401 | } catch (_) { null } | 357 | } catch (_) { |
| 358 | null | ||
| 359 | } | ||
| 402 | }) | 360 | }) |
| 403 | } | 361 | } |
| 404 | }); | 362 | }); |
| @@ -411,5 +369,99 @@ export const generateMaps = async (container, callback) => { | |||
| 411 | onRemove(htmlHolder, () => layoutObserver.disconnect()) | 369 | onRemove(htmlHolder, () => layoutObserver.disconnect()) |
| 412 | //}}} | 370 | //}}} |
| 413 | //}}} | 371 | //}}} |
| 372 | // Render Maps {{{ | ||
| 373 | |||
| 374 | const afterEachMapLoaded = (mapContainer) => { | ||
| 375 | const focusClickedMap = () => { | ||
| 376 | container.querySelectorAll('.map-container') | ||
| 377 | .forEach(c => c.removeAttribute('data-focus')) | ||
| 378 | mapContainer.setAttribute('data-focus', true) | ||
| 379 | } | ||
| 380 | mapContainer.onclick = focusClickedMap | ||
| 381 | mapContainer.setAttribute('tabindex', "-1") | ||
| 382 | |||
| 383 | const observer = mapFocusObserver() | ||
| 384 | mapFocusObserver().observe(mapContainer, { | ||
| 385 | attributes: true, | ||
| 386 | attributeFilter: ["data-focus"], | ||
| 387 | attributeOldValue: true | ||
| 388 | }); | ||
| 389 | onRemove(mapContainer, () => observer.disconnect()) | ||
| 390 | } | ||
| 391 | |||
| 392 | // Set unique ID for map container | ||
| 393 | const mapIdList = [] | ||
| 394 | const assignMapId = (config) => { | ||
| 395 | let mapId = config.id | ||
| 396 | if (!mapId) { | ||
| 397 | mapId = config.use?.split('/')?.at(-1) | ||
| 398 | let counter = 1 | ||
| 399 | while (!mapId || mapIdList.includes(mapId)) { | ||
| 400 | mapId = `${config.use ?? "unnamed"}-${counter}` | ||
| 401 | counter++ | ||
| 402 | } | ||
| 403 | config.id = mapId | ||
| 404 | } | ||
| 405 | mapIdList.push(mapId) | ||
| 406 | return config | ||
| 407 | } | ||
| 408 | |||
| 409 | // Render each code block with "language-map" class | ||
| 410 | const elementsWithMapConfig = Array.from(container.querySelectorAll('pre:has(.language-map)') ?? []) | ||
| 411 | const render = renderWith(config => ({ width: "100%", ...config })) | ||
| 412 | const renderTargets = elementsWithMapConfig | ||
| 413 | .map(async (target) => { | ||
| 414 | // Get text in code block starts with '```map' | ||
| 415 | const configText = target.querySelector('.language-map') | ||
| 416 | .textContent | ||
| 417 | // BE CAREFUL!!! 0xa0 char is "non-breaking spaces" in HTML text content | ||
| 418 | // replace it by normal space | ||
| 419 | .replace(/\u00A0/g, '\u0020') | ||
| 420 | |||
| 421 | let configList = [] | ||
| 422 | try { | ||
| 423 | configList = parseConfigsFromYaml(configText).map(assignMapId) | ||
| 424 | } catch (_) { | ||
| 425 | console.warn('Fail to parse yaml config for element', target) | ||
| 426 | } | ||
| 427 | |||
| 428 | // Render maps | ||
| 429 | return render(target, configList) | ||
| 430 | .then(results => { | ||
| 431 | results.forEach((mapByConfig) => { | ||
| 432 | if (mapByConfig.status === 'fulfilled') { | ||
| 433 | afterEachMapLoaded(mapByConfig.value) | ||
| 434 | return mapByConfig.value | ||
| 435 | } else { | ||
| 436 | console.error('Fail to render target element', mapByConfig.reason) | ||
| 437 | } | ||
| 438 | }) | ||
| 439 | }) | ||
| 440 | }) | ||
| 441 | |||
| 442 | const renderAllTargets = Promise.all(renderTargets) | ||
| 443 | .then(() => { | ||
| 444 | console.info('Finish Rendering') | ||
| 445 | |||
| 446 | const maps = htmlHolder.querySelectorAll('.map-container') ?? [] | ||
| 447 | Array.from(maps) | ||
| 448 | .forEach(ele => { | ||
| 449 | callback(ele) | ||
| 450 | const markers = geoLinks | ||
| 451 | .filter(link => !link.targets || link.targets.includes(ele.id)) | ||
| 452 | .map(link => ({ | ||
| 453 | xy: link.xy, | ||
| 454 | title: link.url.pathname | ||
| 455 | })) | ||
| 456 | ele?.renderer?.addMarkers(markers) | ||
| 457 | }) | ||
| 458 | |||
| 459 | htmlHolder.querySelectorAll('.marker') | ||
| 460 | .forEach(marker => htmlHolder.anchors.push(marker)) | ||
| 461 | |||
| 462 | return maps | ||
| 463 | }) | ||
| 464 | |||
| 465 | //}}} | ||
| 414 | return renderAllTargets | 466 | return renderAllTargets |
| 415 | } | 467 | } |
diff --git a/src/editor.mjs b/src/editor.mjs index b2c4d54..168e66b 100644 --- a/src/editor.mjs +++ b/src/editor.mjs | |||
| @@ -40,7 +40,8 @@ const editor = new EasyMDE({ | |||
| 40 | shortcuts: { | 40 | shortcuts: { |
| 41 | "map": "Ctrl-Alt-M", | 41 | "map": "Ctrl-Alt-M", |
| 42 | "debug": "Ctrl-Alt-D", | 42 | "debug": "Ctrl-Alt-D", |
| 43 | "toggleUnorderedList": "Ctrl-Shift-L", | 43 | "toggleUnorderedList": null, |
| 44 | "toggleOrderedList": null, | ||
| 44 | }, | 45 | }, |
| 45 | toolbar: [ | 46 | toolbar: [ |
| 46 | { | 47 | { |
| @@ -156,12 +157,12 @@ const debounceForMap = (() => { | |||
| 156 | } | 157 | } |
| 157 | })() | 158 | })() |
| 158 | 159 | ||
| 159 | const afterMapRendered = (mapHolder) => { | 160 | const afterMapRendered = (_) => { |
| 160 | mapHolder.oncontextmenu = (event) => { | 161 | // mapHolder.oncontextmenu = (event) => { |
| 161 | event.preventDefault() | 162 | // event.preventDefault() |
| 162 | const lonLat = mapHolder.renderer.unproject([event.x, event.y]) | 163 | // const lonLat = mapHolder.renderer.unproject([event.x, event.y]) |
| 163 | // TODO... | 164 | // // TODO... |
| 164 | } | 165 | // } |
| 165 | } | 166 | } |
| 166 | 167 | ||
| 167 | const updateDumbyMap = () => { | 168 | const updateDumbyMap = () => { |
| @@ -174,7 +175,6 @@ updateDumbyMap() | |||
| 174 | 175 | ||
| 175 | // Re-render HTML by editor content | 176 | // Re-render HTML by editor content |
| 176 | cm.on("change", (_, change) => { | 177 | cm.on("change", (_, change) => { |
| 177 | console.log('change', change.text) | ||
| 178 | updateDumbyMap() | 178 | updateDumbyMap() |
| 179 | addClassToCodeLines() | 179 | addClassToCodeLines() |
| 180 | completeForCodeBlock(change) | 180 | completeForCodeBlock(change) |
| @@ -345,7 +345,6 @@ const handleTypingInCodeBlock = (anchor) => { | |||
| 345 | // }}} | 345 | // }}} |
| 346 | // FUNCTION: get suggestions by current input {{{ | 346 | // FUNCTION: get suggestions by current input {{{ |
| 347 | const getSuggestions = (anchor) => { | 347 | const getSuggestions = (anchor) => { |
| 348 | let suggestions = [] | ||
| 349 | const text = cm.getLine(anchor.line) | 348 | const text = cm.getLine(anchor.line) |
| 350 | 349 | ||
| 351 | // Clear marks on text | 350 | // Clear marks on text |
| @@ -445,7 +444,7 @@ const getSuggestions = (anchor) => { | |||
| 445 | ) | 444 | ) |
| 446 | return rendererSuggestions.length > 0 ? rendererSuggestions : [] | 445 | return rendererSuggestions.length > 0 ? rendererSuggestions : [] |
| 447 | } | 446 | } |
| 448 | return suggestions | 447 | return [] |
| 449 | } | 448 | } |
| 450 | // }}} | 449 | // }}} |
| 451 | // {{{ FUNCTION: Show element about suggestions | 450 | // {{{ FUNCTION: Show element about suggestions |
| @@ -505,11 +504,9 @@ cm.on("cursorActivity", (_) => { | |||
| 505 | }); | 504 | }); |
| 506 | // }}} | 505 | // }}} |
| 507 | // EVENT: keydown for suggestions {{{ | 506 | // EVENT: keydown for suggestions {{{ |
| 507 | const keyForSuggestions = ['Tab', 'Enter', 'Escape'] | ||
| 508 | cm.on('keydown', (_, e) => { | 508 | cm.on('keydown', (_, e) => { |
| 509 | 509 | if (!cm.hasFocus || !keyForSuggestions.includes(e.key) || suggestionsEle.style.display === 'none') return; | |
| 510 | // Only the following keys are used | ||
| 511 | const keyForSuggestions = ['Tab', 'Enter', 'Escape'].includes(e.key) | ||
| 512 | if (!keyForSuggestions || suggestionsEle.style.display === 'none') return; | ||
| 513 | 510 | ||
| 514 | // Directly add a newline when no suggestion is selected | 511 | // Directly add a newline when no suggestion is selected |
| 515 | const currentSuggestion = suggestionsEle.querySelector('.container__suggestion.focus') | 512 | const currentSuggestion = suggestionsEle.querySelector('.container__suggestion.focus') |
| @@ -545,6 +542,13 @@ cm.on('keydown', (_, e) => { | |||
| 545 | document.onkeydown = (e) => { | 542 | document.onkeydown = (e) => { |
| 546 | if (e.altKey && e.ctrlKey && e.key === 'm') { | 543 | if (e.altKey && e.ctrlKey && e.key === 'm') { |
| 547 | toggleEditing() | 544 | toggleEditing() |
| 545 | e.preventDefault() | ||
| 546 | return null | ||
| 547 | } | ||
| 548 | if (cm.hasFocus()) { | ||
| 549 | return null | ||
| 550 | } else { | ||
| 551 | return e | ||
| 548 | } | 552 | } |
| 549 | } | 553 | } |
| 550 | 554 | ||