From 9cdc8b1e6b4f57bb2d6ec5d756bcd5e348afcd96 Mon Sep 17 00:00:00 2001 From: Hsieh Chin Fan Date: Wed, 11 Sep 2024 20:35:09 +0800 Subject: [PATCH 01/46] chore(release): 0.2.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f82c960..e913b2c 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "dumbymap", "description": "Generate interactive maps from Semantic HTML", - "version": "0.2.0", + "version": "0.2.1", "license": "MIT", "type": "module", "main": "dist/dumbymap.mjs", From 6fad3f93ec8a92e71138853e36e0fd2526cc2942 Mon Sep 17 00:00:00 2001 From: Hsieh Chin Fan Date: Thu, 12 Sep 2024 11:36:50 +0800 Subject: [PATCH 02/46] feat: export method createGeoLinks() --- src/dumbymap.mjs | 69 ++++++++++++++++++++++++++---------------------- 1 file changed, 37 insertions(+), 32 deletions(-) diff --git a/src/dumbymap.mjs b/src/dumbymap.mjs index a896d0d..f5cab8e 100644 --- a/src/dumbymap.mjs +++ b/src/dumbymap.mjs @@ -25,7 +25,7 @@ const onRemove = (element, callback) => { obs.observe(parent, { childList: true, }); } // }}} -// FUNCTION: Get DocLinks from normal special anchor element {{{ +// FUNCTION: Get DocLinks from special anchor element {{{ const docLinkSelector = 'a[href^="#"][title^="=>"]' export const createDocLinks = (container) => Array.from(container.querySelectorAll(docLinkSelector)) @@ -60,6 +60,30 @@ export const createDocLinks = (container) => Array.from(container.querySelectorA return link }) // }}} +// FUNCTION: Get GeoLinks from special anchor element {{{ +// Links points to map by geo schema and id +const geoLinkSelector = 'a[href^="geo:"]' +export const createGeoLinks = (container, callback) => Array.from(container.querySelectorAll(geoLinkSelector)) + .filter(link => { + const url = new URL(link.href) + const xy = url?.href?.match(/^geo:([0-9.,]+)/)?.at(1)?.split(',')?.reverse()?.map(Number) + + if (!xy || isNaN(xy[0]) || isNaN(xy[1])) return false + + // Geo information in link + link.url = url + link.xy = xy + link.classList.add('with-leader-line', 'geolink') + link.targets = link.url.searchParams.get('id')?.split(',') ?? null + + // LeaderLine + link.lines = [] + callback(link) + + return true + }) + +// }}} export const markdown2HTML = (container, mdContent) => { // Render: Markdown -> HTML {{{ Array.from(container.children).map(e => e.remove()) @@ -119,6 +143,18 @@ export const generateMaps = async (container) => { // Get anchors with "geo:" scheme const htmlHolder = container.querySelector('.SemanticHtml') ?? container htmlHolder.anchors = [] + const geoLinks = createGeoLinks(htmlHolder, (link) => { + link.onmouseover = () => addLeaderLines(link) + link.onmouseout = () => removeLeaderLines(link) + link.onclick = (event) => { + event.preventDefault() + htmlHolder.anchors + .filter(isAnchorPointedBy(link)) + .forEach(updateMapByMarker(link.xy)) + // TODO Just hide leader line and show it again + removeLeaderLines(link) + } + }) // Set focusArea const showcase = document.createElement('div') @@ -128,36 +164,6 @@ export const generateMaps = async (container) => { mapPlaceholder.id = 'mapPlaceholder' showcase.appendChild(mapPlaceholder) - // Links points to map by geo schema and id - const geoLinks = Array.from(htmlHolder.querySelectorAll('a[href^="geo:"]')) - .filter(link => { - const url = new URL(link.href) - const xy = url?.href?.match(/^geo:([0-9.,]+)/)?.at(1)?.split(',')?.reverse()?.map(Number) - - if (!xy || isNaN(xy[0]) || isNaN(xy[1])) return false - - // Geo information in link - link.url = url - link.xy = xy - link.classList.add('with-leader-line', 'geolink') - link.targets = link.url.searchParams.get('id')?.split(',') ?? null - - // LeaderLine - link.lines = [] - link.onmouseover = () => addLeaderLines(link) - link.onmouseout = () => removeLeaderLines(link) - link.onclick = (event) => { - event.preventDefault() - htmlHolder.anchors - .filter(isAnchorPointedBy(link)) - .forEach(updateMapByMarker(xy)) - // TODO Just hide leader line and show it again - removeLeaderLines(link) - } - - return true - }) - const isAnchorPointedBy = (link) => (anchor) => { const mapContainer = anchor.closest('.map-container') const isTarget = !link.targets || link.targets.includes(mapContainer.id) @@ -276,7 +282,6 @@ export const generateMaps = async (container) => { Object.assign(result, { markers: markersFromLinks }) return result }) - /* eslint-disable no-unused-vars */ } catch (_) { console.warn('Fail to parse yaml config for element', target) } From 5c35881befd7a3dc51afd124ed28cc4ecb1bb827 Mon Sep 17 00:00:00 2001 From: Hsieh Chin Fan Date: Thu, 12 Sep 2024 11:38:08 +0800 Subject: [PATCH 03/46] chore: Quick way to evenly split layout --- src/css/index.css | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/css/index.css b/src/css/index.css index 3764dac..55e3668 100644 --- a/src/css/index.css +++ b/src/css/index.css @@ -10,15 +10,18 @@ body { } .result-html { - flex: 0 0 50%; + order: 2; + flex: 0 0 49%; + height: calc(100vh - 15px); + margin: 10px; + overflow-y: scroll; border: var(--content-border); border-radius: var(--content-border-radius); - margin: 10px; } .editor { - flex: 0 0 50%; - max-width: 50vw; + order: 1; + flex: 0 0 48%; height: calc(100vh - 15px); margin: 10px; } From 2a477eea4cd607a23f36b8989aec58e73100f36b Mon Sep 17 00:00:00 2001 From: Hsieh Chin Fan Date: Thu, 12 Sep 2024 11:39:39 +0800 Subject: [PATCH 04/46] fix: Update class name for map showcase --- src/css/dumbymap.css | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/css/dumbymap.css b/src/css/dumbymap.css index 383c4ec..f04ed8b 100644 --- a/src/css/dumbymap.css +++ b/src/css/dumbymap.css @@ -38,7 +38,7 @@ overflow-x: scroll; /* height: 100vh; */ - #map { + .Showcase { flex: 0; #mapPlaceholder { display: none; @@ -47,7 +47,7 @@ .SemanticHtml { flex: 1; - padding: 20px; + padding: 0.5rem; overflow-y: scroll; > .draggable-block { @@ -99,7 +99,7 @@ width: fit-content; } - #map .map-container { + .Showcase .map-container { width: 100% !important; height: 100% !important; } @@ -120,7 +120,7 @@ } .result-html[data-layout=side] { - #map, + .Showcase, .SemanticHtml { flex: 50%; overflow-y: scroll; @@ -129,7 +129,7 @@ } .result-html[data-layout=overlay] { - #map, + .Showcase, .SemanticHtml { position: fixed; height: 100%; From a67e5adede9fca1326e50bd530861fae75b2bf3d Mon Sep 17 00:00:00 2001 From: Hsieh Chin Fan Date: Thu, 12 Sep 2024 11:53:29 +0800 Subject: [PATCH 05/46] style: Reformat code --- src/dumbymap.mjs | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/src/dumbymap.mjs b/src/dumbymap.mjs index f5cab8e..46682c9 100644 --- a/src/dumbymap.mjs +++ b/src/dumbymap.mjs @@ -126,13 +126,11 @@ export const markdown2HTML = (container, mdContent) => { const contentWithToc = '${toc}\n\n\n' + mdContent htmlHolder.innerHTML = md.render(contentWithToc); + // TODO Do this in markdown-it - htmlHolder.querySelectorAll('*> div:not(:has(nav))') + htmlHolder.querySelectorAll('* > div:not(:has(nav))') .forEach(b => b.classList.add('draggable-block')) - - // TODO Improve it! - return container //}}} } @@ -156,14 +154,6 @@ export const generateMaps = async (container) => { } }) - // Set focusArea - const showcase = document.createElement('div') - container.appendChild(showcase) - showcase.classList.add('Showcase') - const mapPlaceholder = document.createElement('div') - mapPlaceholder.id = 'mapPlaceholder' - showcase.appendChild(mapPlaceholder) - const isAnchorPointedBy = (link) => (anchor) => { const mapContainer = anchor.closest('.map-container') const isTarget = !link.targets || link.targets.includes(mapContainer.id) @@ -304,6 +294,15 @@ export const generateMaps = async (container) => { //}}} // CSS observer {{{ + + // Set focusArea + const showcase = document.createElement('div') + container.appendChild(showcase) + showcase.classList.add('Showcase') + const mapPlaceholder = document.createElement('div') + mapPlaceholder.id = 'mapPlaceholder' + showcase.appendChild(mapPlaceholder) + // Layout{{{ // press key to switch layout From a6c7ca4626d495580c7a60dee7d0f5ccb090a629 Mon Sep 17 00:00:00 2001 From: Hsieh Chin Fan Date: Thu, 12 Sep 2024 16:28:47 +0800 Subject: [PATCH 06/46] chore: Fix warning from eslint --- eslint.config.js | 6 +++++- src/editor.mjs | 6 ++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/eslint.config.js b/eslint.config.js index ac88ddf..228a920 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -21,7 +21,11 @@ export default [ node: nodePlugin, }, rules: { - 'no-unused-vars': ['warn', { 'varsIgnorePattern': '^_' }], + 'no-unused-vars': ['warn', { + 'varsIgnorePattern': '^_', + "argsIgnorePattern": "^_", + "caughtErrorsIgnorePattern": "^ignore|_", + }], 'import/no-unresolved': 'error', 'no-console': ["error", { allow: ["info", "warn", "error"] }], 'eqeqeq': ['error', 'always'], diff --git a/src/editor.mjs b/src/editor.mjs index beee35b..5470b64 100644 --- a/src/editor.mjs +++ b/src/editor.mjs @@ -1,3 +1,5 @@ +/*global EasyMDE*/ +/*eslint no-undef: "error"*/ import { markdown2HTML, generateMaps } from './dumbymap' import { defaultAliasesForRenderer, parseConfigsFromYaml } from 'mapclay' import { createDocLinks } from './dumbymap.mjs' @@ -270,7 +272,7 @@ const getSuggestions = (anchor) => { .then(rendererModule => { rendererOptions[renderer] = rendererModule.default.validOptions }) - .catch(() => { + .catch(_ => { markInputIsInvalid(lineWithRenderer) console.warn(`Fail to get valid options from renderer with URL ${rendererUrl}`) }) @@ -406,6 +408,7 @@ cm.on('keydown', (_, e) => { const focusSuggestion = e.shiftKey ? previousSuggestion : nextSuggestion // Current editor selection state + const anchor = cm.getCursor() switch (e.key) { case 'Tab': Array.from(suggestionsEle.children).forEach(s => s.classList.remove('focus')) @@ -416,7 +419,6 @@ cm.on('keydown', (_, e) => { currentSuggestion.onclick() break; case 'Escape': - const anchor = cm.getCursor() suggestionsEle.style.display = 'none'; // Focus editor again setTimeout(() => cm.focus() && cm.setCursor(anchor), 100) From 2923d18a91b40695e6e6c229cf6a0d7f7fbced3e Mon Sep 17 00:00:00 2001 From: Hsieh Chin Fan Date: Thu, 12 Sep 2024 16:30:10 +0800 Subject: [PATCH 07/46] chore(release): 0.2.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e913b2c..1ff8900 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "dumbymap", "description": "Generate interactive maps from Semantic HTML", - "version": "0.2.1", + "version": "0.2.2", "license": "MIT", "type": "module", "main": "dist/dumbymap.mjs", From d0b122997dc8555953ab2b8e3f2cd5b2f950b69b Mon Sep 17 00:00:00 2001 From: Hsieh Chin Fan Date: Thu, 12 Sep 2024 18:51:56 +0800 Subject: [PATCH 08/46] fix: terra-draw display on leaflet --- src/css/style.css | 1 - 1 file changed, 1 deletion(-) diff --git a/src/css/style.css b/src/css/style.css index 9b458a9..83b8045 100644 --- a/src/css/style.css +++ b/src/css/style.css @@ -10,7 +10,6 @@ img, picture, video, canvas, svg { display: block; - max-width: 100%; } input, button, textarea, select { From 2d3fc98896a9504e2d4b20df43fc40b15f9b48e2 Mon Sep 17 00:00:00 2001 From: Hsieh Chin Fan Date: Thu, 12 Sep 2024 19:10:10 +0800 Subject: [PATCH 09/46] fix: Prevent editor cover screen --- src/css/index.css | 1 + 1 file changed, 1 insertion(+) diff --git a/src/css/index.css b/src/css/index.css index 55e3668..93a4918 100644 --- a/src/css/index.css +++ b/src/css/index.css @@ -22,6 +22,7 @@ body { .editor { order: 1; flex: 0 0 48%; + max-width: 50vw; height: calc(100vh - 15px); margin: 10px; } From 63a0a58a4b7ae086a887ffabb6c764963e62cd2e Mon Sep 17 00:00:00 2001 From: Hsieh Chin Fan Date: Thu, 12 Sep 2024 19:10:22 +0800 Subject: [PATCH 10/46] chore: Update package dependencies --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 1ff8900..4a641e1 100644 --- a/package.json +++ b/package.json @@ -40,12 +40,12 @@ "eslint-plugin-node": "^11.1.0", "eslint-plugin-promise": "^7.1.0", "globals": "^15.9.0", - "rollup": "^4.21.2" + "rollup": "^4.21.3" }, "dependencies": { "easymde": "^2.18.0", "leader-line": "^1.0.7", - "mapclay": "^0.6.1", + "mapclay": "^0.6.2", "markdown-it": "^14.1.0", "markdown-it-anchor": "^9.2.0", "markdown-it-footnote": "^4.0.0", From f1f5462fadd410dbbb6c284481b6ca7219313e64 Mon Sep 17 00:00:00 2001 From: Hsieh Chin Fan Date: Fri, 13 Sep 2024 18:52:01 +0800 Subject: [PATCH 11/46] feat: Style code block even line is empty --- src/css/index.css | 3 ++- src/editor.mjs | 19 ++++++++++++++++++- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/src/css/index.css b/src/css/index.css index 93a4918..0997779 100644 --- a/src/css/index.css +++ b/src/css/index.css @@ -64,7 +64,8 @@ body { } /* FIXME For those empty line (no child with cm-comment) */ -.CodeMirror-line:has(.cm-comment) { +.inside-code-block, +.CodeMirror-line:has(.cm-formatting-code-block) { background: rgba(0,0,0,.05) !important; .cm-comment { diff --git a/src/editor.mjs b/src/editor.mjs index 5470b64..d1b4ba1 100644 --- a/src/editor.mjs +++ b/src/editor.mjs @@ -89,10 +89,27 @@ if (queryParams.get('render')) { toggleMaps(HtmlContainer) } +// Quick hack to style lines inside code block +const addClassToCodeLines = () => { + const lines = cm.getLineHandle(0).parent.lines + let insideCodeBlock = false + lines.forEach((line, index) => { + if (line.text.match(/^````*/)) { + insideCodeBlock = !insideCodeBlock + } else if (insideCodeBlock) { + cm.addLineClass(index, "text", "inside-code-block") + } else { + cm.removeLineClass(index, "text", "inside-code-block") + } + }) +} +addClassToCodeLines() + // Re-render HTML by editor content -cm.on("change", () => { +cm.on("change", (_, obj) => { markdown2HTML(HtmlContainer, editor.value()) createDocLinks(HtmlContainer) + addClassToCodeLines() }) // }}} From e8a94e5e43a306753b99615554e76a0815df090e Mon Sep 17 00:00:00 2001 From: Hsieh Chin Fan Date: Fri, 13 Sep 2024 18:52:45 +0800 Subject: [PATCH 12/46] fix: Logic about suggestions * Switch to event 'onCursorActivity' for suggestions * Show suggestion about Renderer without case sensitive --- src/editor.mjs | 59 ++++++++++++++++++++++++++++++-------------------- 1 file changed, 35 insertions(+), 24 deletions(-) diff --git a/src/editor.mjs b/src/editor.mjs index d1b4ba1..bf59e47 100644 --- a/src/editor.mjs +++ b/src/editor.mjs @@ -166,21 +166,24 @@ fetch(defaultApply) // return true // } // }}} -// FUNCTION Check if current token is inside code block {{{ -const insideCodeblockForMap = (token) => - token.state.overlay.codeBlock && !token.string.match(/^````*/) +// FUNCTION: Check if current token is inside code block {{{ +const insideCodeblockForMap = (anchor) => { + const token = cm.getTokenAt(anchor) + const result = token.state.overlay.codeBlock && !cm.getLine(anchor.line).match(/^````*/) + return result +} // }}} // FUNCTION: Get renderer by cursor position in code block {{{ const getLineWithRenderer = (anchor) => { const currentLine = anchor.line const match = (line) => cm.getLine(line).match(/^use: /) + if (match(currentLine)) return currentLine - const getToken = (line) => cm.getTokenAt({ line: line, ch: 1 }) // Look backward/forward for pattern of used renderer: /use: .+/ let ps = currentLine - 1 - while (ps > 0 && insideCodeblockForMap(getToken(ps))) { + while (ps > 0 && insideCodeblockForMap(anchor)) { if (match(ps)) { return ps } else if (cm.getLine(ps).match(/^---/)) { @@ -191,11 +194,10 @@ const getLineWithRenderer = (anchor) => { } let ns = currentLine + 1 - while (insideCodeblockForMap(getToken(ns))) { + while (insideCodeblockForMap(anchor)) { if (match(ns)) { return ns } else if (cm.getLine(ns).match(/^---/)) { - // If yaml doc separator is found return null } ns = ns + 1 @@ -265,18 +267,22 @@ const handleTypingInCodeBlock = (anchor) => { // }}} // FUNCTION: get suggestions by current input {{{ const getSuggestions = (anchor) => { - const text = cm.getLine(anchor.line) - const markInputIsInvalid = () => cm.getDoc().markText( - { ...anchor, ch: 0 }, - { ...anchor, ch: -1 }, - { className: 'invalid-input' }, - ) let suggestions = [] + const text = cm.getLine(anchor.line) + const markInputIsInvalid = () => { + cm.getDoc().markText( + { ...anchor, ch: 0 }, + { ...anchor, ch: -1 }, + { className: 'invalid-input' }, + ) + } // Check if "use: " is set const lineWithRenderer = getLineWithRenderer(anchor) - const renderer = cm.getLine(lineWithRenderer).split(' ')[1] - if (renderer) { + const renderer = lineWithRenderer + ? cm.getLine(lineWithRenderer).split(' ')[1] + : null + if (renderer && anchor.line !== lineWithRenderer) { // Do not check properties if (text.startsWith(' ')) return [] @@ -288,10 +294,14 @@ const getSuggestions = (anchor) => { import(rendererUrl) .then(rendererModule => { rendererOptions[renderer] = rendererModule.default.validOptions + const currentAnchor = cm.getCursor() + if (insideCodeblockForMap(currentAnchor)) { + handleTypingInCodeBlock(currentAnchor) + } }) .catch(_ => { markInputIsInvalid(lineWithRenderer) - console.warn(`Fail to get valid options from renderer with URL ${rendererUrl}`) + console.warn(`Fail to get valid options from Renderer typed: ${renderer}`) }) return [] } @@ -336,10 +346,10 @@ const getSuggestions = (anchor) => { const rendererSuggestions = Object.entries(defaultAliasesForRenderer.use) .filter(([renderer,]) => { const suggestion = `use: ${renderer}` - const suggetionNoSpace = suggestion.replace(' ', '') - const textNoSpace = text.replace(' ', '') + const suggestionPattern = suggestion.replace(' ', '').toLowerCase() + const textPattern = text.replace(' ', '').toLowerCase() return suggestion !== text && - (suggetionNoSpace.includes(textNoSpace)) + (suggestionPattern.includes(textPattern)) }) .map(([renderer, info]) => new Suggestion({ @@ -392,15 +402,16 @@ const addSuggestions = (anchor, suggestions) => { const rect = suggestionsEle.getBoundingClientRect() suggestionsEle.style.maxWidth = `calc(${window.innerWidth}px - ${rect.x}px - 2rem)`; suggestionsEle.style.display = 'block' + suggestionsEle.style.transform = 'translate(2em, 1em)' } // }}} -// EVENT: suggests for current selection {{{ +// EVENT: Suggests for current selection {{{ // FIXME Dont show suggestion when selecting multiple chars -cm.on("beforeSelectionChange", (_, obj) => { - const anchor = (obj.ranges[0].anchor) - const token = cm.getTokenAt(anchor) +cm.on("cursorActivity", (_) => { + suggestionsEle.style.display = 'none' + const anchor = cm.getCursor() - if (insideCodeblockForMap(token)) { + if (insideCodeblockForMap(anchor)) { handleTypingInCodeBlock(anchor) } }); From e4f487ed9002889d745dbc9770744f866e8749bb Mon Sep 17 00:00:00 2001 From: Hsieh Chin Fan Date: Fri, 13 Sep 2024 18:53:04 +0800 Subject: [PATCH 13/46] chore: Remove button of fullscreen mode --- src/editor.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/editor.mjs b/src/editor.mjs index bf59e47..7cb0525 100644 --- a/src/editor.mjs +++ b/src/editor.mjs @@ -77,7 +77,7 @@ const editor = new EasyMDE({ navigator.clipboard.writeText(window.location.href) alert('URL copied to clipboard') }, - }, 'undo', 'redo', '|', 'heading-1', 'heading-2', '|', 'link', 'image', '|', 'bold', 'italic', 'strikethrough', 'code', 'clean-block', '|', 'unordered-list', 'ordered-list', 'quote', 'table', '|', 'fullscreen' + }, 'undo', 'redo', '|', 'heading-1', 'heading-2', '|', 'link', 'image', '|', 'bold', 'italic', 'strikethrough', 'code', 'clean-block', '|', 'unordered-list', 'ordered-list', 'quote', 'table' ], }); From a9e16726b0e003ebb30e0c9bbeb8288a68810228 Mon Sep 17 00:00:00 2001 From: Hsieh Chin Fan Date: Fri, 13 Sep 2024 18:54:09 +0800 Subject: [PATCH 14/46] feat: Use monospace font in editor --- src/css/index.css | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/css/index.css b/src/css/index.css index 0997779..7412835 100644 --- a/src/css/index.css +++ b/src/css/index.css @@ -40,6 +40,9 @@ body { border-radius: var(--content-border-radius); padding-inline: 0; + font-family: monospace; + font-size: 1.0rem; + span { white-space: pre; } From 444e66905207efac253320d8558d5922d4f8291a Mon Sep 17 00:00:00 2001 From: Hsieh Chin Fan Date: Fri, 13 Sep 2024 19:10:13 +0800 Subject: [PATCH 15/46] patch --- src/editor.mjs | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/src/editor.mjs b/src/editor.mjs index 7cb0525..2df4279 100644 --- a/src/editor.mjs +++ b/src/editor.mjs @@ -173,7 +173,7 @@ const insideCodeblockForMap = (anchor) => { return result } // }}} -// FUNCTION: Get renderer by cursor position in code block {{{ +// FUNCTION: Get Renderer by cursor position in code block {{{ const getLineWithRenderer = (anchor) => { const currentLine = anchor.line const match = (line) => cm.getLine(line).match(/^use: /) @@ -182,25 +182,26 @@ const getLineWithRenderer = (anchor) => { // Look backward/forward for pattern of used renderer: /use: .+/ - let ps = currentLine - 1 - while (ps > 0 && insideCodeblockForMap(anchor)) { - if (match(ps)) { - return ps - } else if (cm.getLine(ps).match(/^---/)) { - // If yaml doc separator is found + let pl = currentLine - 1 + while (pl > 0 && insideCodeblockForMap(anchor)) { + const text = cm.getLine(pl) + if (match(pl)) { + return pl + } else if (text.match(/^---|^````*/)) { break } - ps = ps - 1 + pl = pl - 1 } - let ns = currentLine + 1 + let nl = currentLine + 1 while (insideCodeblockForMap(anchor)) { - if (match(ns)) { - return ns - } else if (cm.getLine(ns).match(/^---/)) { + const text = cm.getLine(nl) + if (match(nl)) { + return nl + } else if (text.match(/^---|^````*/)) { return null } - ns = ns + 1 + nl = nl + 1 } return null From 7f3faf7dd4cbb1a42775b17ac4a7ed93749e0917 Mon Sep 17 00:00:00 2001 From: Hsieh Chin Fan Date: Fri, 13 Sep 2024 20:01:04 +0800 Subject: [PATCH 16/46] style: More code blocks --- src/editor.mjs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/editor.mjs b/src/editor.mjs index 2df4279..b99e34f 100644 --- a/src/editor.mjs +++ b/src/editor.mjs @@ -4,7 +4,7 @@ import { markdown2HTML, generateMaps } from './dumbymap' import { defaultAliasesForRenderer, parseConfigsFromYaml } from 'mapclay' import { createDocLinks } from './dumbymap.mjs' -// Set up Editor {{{ +// Set up Containers {{{ const HtmlContainer = document.querySelector(".result-html") const textArea = document.querySelector(".editor textarea") @@ -19,6 +19,8 @@ const toggleMaps = (container) => { container.setAttribute('data-layout', 'none') } } +// }}} +// Set up EasyMDE {{{ // Content values for editor const getStateFromHash = (hash) => { @@ -40,7 +42,6 @@ const contentFromHash = initialState.content const lastContent = localStorage.getItem('editorContent') const defaultContent = '## Links\n\n- [Go to marker](geo:24,121?id=foo,leaflet&text=normal "Link Test")\n\n```map\nid: foo\nuse: Maplibre\n```\n' -// Set up EasyMDE {{{ const editor = new EasyMDE({ element: textArea, indentWithTabs: false, @@ -82,6 +83,8 @@ const editor = new EasyMDE({ }); const cm = editor.codemirror +// }}} +// Set up logic about editor content {{{ markdown2HTML(HtmlContainer, editor.value()) createDocLinks(HtmlContainer) @@ -111,7 +114,6 @@ cm.on("change", (_, obj) => { createDocLinks(HtmlContainer) addClassToCodeLines() }) -// }}} // Reload editor content by hash value window.onhashchange = () => { @@ -176,6 +178,8 @@ const insideCodeblockForMap = (anchor) => { // FUNCTION: Get Renderer by cursor position in code block {{{ const getLineWithRenderer = (anchor) => { const currentLine = anchor.line + if (!cm.getLine) return null + const match = (line) => cm.getLine(line).match(/^use: /) if (match(currentLine)) return currentLine From 071e07350c85a6376aa5bd3b900a59ebf6c304ee Mon Sep 17 00:00:00 2001 From: Hsieh Chin Fan Date: Fri, 13 Sep 2024 20:01:24 +0800 Subject: [PATCH 17/46] style: Reformat code --- src/dumbymap.mjs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/dumbymap.mjs b/src/dumbymap.mjs index 46682c9..a34998f 100644 --- a/src/dumbymap.mjs +++ b/src/dumbymap.mjs @@ -258,9 +258,11 @@ export const generateMaps = async (container) => { const renderTargets = Array.from(container.querySelectorAll('pre:has(.language-map)')) const renderAllTargets = renderTargets.map(async (target) => { // Get text in code block starts with '```map' - // BE CAREFUL!!! 0xa0 char is "non-breaking spaces" in HTML text content - // replace it by normal space - const configText = target.querySelector('.language-map').textContent.replace(/\u00A0/g, '\u0020') + const configText = target.querySelector('.language-map') + .textContent + // BE CAREFUL!!! 0xa0 char is "non-breaking spaces" in HTML text content + // replace it by normal space + .replace(/\u00A0/g, '\u0020') let configList = [] try { From e84ec00be6416c8dd1578d5a4589fa59ea1d92ee Mon Sep 17 00:00:00 2001 From: Hsieh Chin Fan Date: Fri, 13 Sep 2024 21:20:37 +0800 Subject: [PATCH 18/46] feat: Completion for text sequence * Type "-" or "`", autocomplet for YMAL doc separator or code fence * Delete a char of YAML doc separator or code fence, auto empty current line * Prevent more content after YAML doc separator --- src/editor.mjs | 87 ++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 66 insertions(+), 21 deletions(-) diff --git a/src/editor.mjs b/src/editor.mjs index b99e34f..67df938 100644 --- a/src/editor.mjs +++ b/src/editor.mjs @@ -97,7 +97,7 @@ const addClassToCodeLines = () => { const lines = cm.getLineHandle(0).parent.lines let insideCodeBlock = false lines.forEach((line, index) => { - if (line.text.match(/^````*/)) { + if (line.text.match(/^[\u0060]{3}/)) { insideCodeBlock = !insideCodeBlock } else if (insideCodeBlock) { cm.addLineClass(index, "text", "inside-code-block") @@ -108,11 +108,59 @@ const addClassToCodeLines = () => { } addClassToCodeLines() +const completeForCodeBlock = (change) => { + const line = change.to.line + if (change.origin === "+input") { + const text = change.text[0] + + // Completion for YAML doc separator + if (text === "-" && change.to.ch === 0 && insideCodeblockForMap(cm.getCursor())) { + cm.setSelection({ line: line, ch: 0 }, { line: line, ch: 1 }) + cm.replaceSelection(text.repeat(3)) + } + + // Completion for Code fence + if (text === "`" && change.to.ch === 0) { + cm.setSelection({ line: line, ch: 0 }, { line: line, ch: 1 }) + cm.replaceSelection(text.repeat(3)) + const numberOfFences = cm.getValue() + .split('\n') + .filter(line => line.match(/[\u0060]{3}/)) + .length + if (numberOfFences % 2 === 1) { + cm.replaceSelection('map\n\n```') + cm.setCursor({ line: line + 1 }) + } + } + } + + // For YAML doc separator,
and code fence + // Auto delete to start of line + if (change.origin === "+delete") { + const match = change.removed[0].match(/^[-\u0060]$/)?.at(0) + if (match && cm.getLine(line) === match.repeat(2) && match) { + cm.setSelection({ line: line, ch: 0 }, { line: line, ch: 2 }) + cm.replaceSelection('') + } + } +} + // Re-render HTML by editor content -cm.on("change", (_, obj) => { +cm.on("change", (_, change) => { markdown2HTML(HtmlContainer, editor.value()) createDocLinks(HtmlContainer) addClassToCodeLines() + completeForCodeBlock(change) +}) + +cm.on("beforeChange", (_, change) => { + const line = change.to.line + // Don't allow more content after YAML doc separator + if (change.origin.match(/^(\+input|paste)$/)) { + if (cm.getLine(line) === "---" && change.text[0] !== "") { + change.cancel() + } + } }) // Reload editor content by hash value @@ -153,26 +201,23 @@ fetch(defaultApply) }) .catch(err => console.warn(`Fail to get aliases from ${defaultApply}`, err)) // }}} -// FUNCTION: Check cursor is inside map code block {{{ -// const insideCodeblockForMap = (currentLine) => { -// let tokens = cm.getLineTokens(currentLine) -// -// if (!tokens.includes("comment") || tokens.includes('formatting-code-block')) return false -// -// do { -// line = line - 1 -// if (line < 0) return false -// tokens = cm.getLineTokens(line) -// } while (!tokens.includes('formatting-code-block')) -// -// return true -// } -// }}} // FUNCTION: Check if current token is inside code block {{{ const insideCodeblockForMap = (anchor) => { const token = cm.getTokenAt(anchor) - const result = token.state.overlay.codeBlock && !cm.getLine(anchor.line).match(/^````*/) - return result + const insideCodeBlock = token.state.overlay.codeBlock && !cm.getLine(anchor.line).match(/^[\u0060]{3}/) + if (!insideCodeBlock) return false + + let line = anchor.line - 1 + while (line >= 0) { + const content = cm.getLine(line) + if (content === '```map') { + return true + } else if (content === '```'){ + return false + } + line = line - 1 + } + return false } // }}} // FUNCTION: Get Renderer by cursor position in code block {{{ @@ -191,7 +236,7 @@ const getLineWithRenderer = (anchor) => { const text = cm.getLine(pl) if (match(pl)) { return pl - } else if (text.match(/^---|^````*/)) { + } else if (text.match(/^---|^[\u0060]{3}/)) { break } pl = pl - 1 @@ -202,7 +247,7 @@ const getLineWithRenderer = (anchor) => { const text = cm.getLine(nl) if (match(nl)) { return nl - } else if (text.match(/^---|^````*/)) { + } else if (text.match(/^---|^[\u0060]{3}/)) { return null } nl = nl + 1 From 9cf3a8dfed5c2cc06d3713e1c178ba24387a23ce Mon Sep 17 00:00:00 2001 From: Hsieh Chin Fan Date: Fri, 13 Sep 2024 21:22:46 +0800 Subject: [PATCH 19/46] chore(release): 0.2.3 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 4a641e1..e66a45b 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "dumbymap", "description": "Generate interactive maps from Semantic HTML", - "version": "0.2.2", + "version": "0.2.3", "license": "MIT", "type": "module", "main": "dist/dumbymap.mjs", From 8798cbf48e2c16fa8999147ffce481d4131ec6f2 Mon Sep 17 00:00:00 2001 From: Hsieh Chin Fan Date: Fri, 13 Sep 2024 23:15:10 +0800 Subject: [PATCH 20/46] refactor: Use hidden element as anchor of suggestion list Since suggestion list is not widget anymore, it could be seen outside of editor --- src/editor.mjs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/editor.mjs b/src/editor.mjs index 67df938..af3f629 100644 --- a/src/editor.mjs +++ b/src/editor.mjs @@ -180,6 +180,7 @@ window.onhashchange = () => { // Elements about suggestions {{{ const suggestionsEle = document.createElement('div') suggestionsEle.classList.add('container__suggestions'); +document.body.append(suggestionsEle) const rendererOptions = {} @@ -448,11 +449,13 @@ const addSuggestions = (anchor, suggestions) => { suggestionsEle.appendChild(option); }); - cm.addWidget(anchor, suggestionsEle, true) - const rect = suggestionsEle.getBoundingClientRect() - suggestionsEle.style.maxWidth = `calc(${window.innerWidth}px - ${rect.x}px - 2rem)`; + const widgetAnchor = document.createElement('div') + cm.addWidget(anchor, widgetAnchor, true) + const rect = widgetAnchor.getBoundingClientRect() + suggestionsEle.style.left = `calc(${rect.left}px + 2rem)`; + suggestionsEle.style.top = `calc(${rect.bottom}px + 1rem)`; + suggestionsEle.style.maxWidth = `calc(${window.innerWidth}px - ${rect.x}px - 3rem)`; suggestionsEle.style.display = 'block' - suggestionsEle.style.transform = 'translate(2em, 1em)' } // }}} // EVENT: Suggests for current selection {{{ From 7ef54be57586847c679b9a72b5bb4c546ca61b14 Mon Sep 17 00:00:00 2001 From: Hsieh Chin Fan Date: Sat, 14 Sep 2024 10:56:27 +0800 Subject: [PATCH 21/46] refactor(CSS): Rename class name for HTML holder result-html => DumbyMap --- index.html | 2 +- src/css/dumbymap.css | 8 ++++---- src/css/index.css | 2 +- src/editor.mjs | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/index.html b/index.html index 28b25c3..196458e 100644 --- a/index.html +++ b/index.html @@ -22,7 +22,7 @@ -
+
diff --git a/src/css/dumbymap.css b/src/css/dumbymap.css index f04ed8b..ba074a2 100644 --- a/src/css/dumbymap.css +++ b/src/css/dumbymap.css @@ -29,7 +29,7 @@ } } -.result-html { +.DumbyMap { position: relative; max-width: 60em; margin: 0 auto; @@ -105,7 +105,7 @@ } } -.result-html[data-layout]:not([data-layout=none]) { +.DumbyMap[data-layout]:not([data-layout=none]) { margin: 0; height: 100vh; @@ -119,7 +119,7 @@ } } -.result-html[data-layout=side] { +.DumbyMap[data-layout=side] { .Showcase, .SemanticHtml { flex: 50%; @@ -128,7 +128,7 @@ } } -.result-html[data-layout=overlay] { +.DumbyMap[data-layout=overlay] { .Showcase, .SemanticHtml { position: fixed; diff --git a/src/css/index.css b/src/css/index.css index 7412835..9e75e93 100644 --- a/src/css/index.css +++ b/src/css/index.css @@ -9,7 +9,7 @@ body { width: 100%; } -.result-html { +.DumbyMap { order: 2; flex: 0 0 49%; height: calc(100vh - 15px); diff --git a/src/editor.mjs b/src/editor.mjs index af3f629..915ce24 100644 --- a/src/editor.mjs +++ b/src/editor.mjs @@ -6,7 +6,7 @@ import { createDocLinks } from './dumbymap.mjs' // Set up Containers {{{ -const HtmlContainer = document.querySelector(".result-html") +const HtmlContainer = document.querySelector(".DumbyMap") const textArea = document.querySelector(".editor textarea") const toggleMaps = (container) => { From 901ad1a83ae08c259f75c69e5cc957a72e18f762 Mon Sep 17 00:00:00 2001 From: Hsieh Chin Fan Date: Sat, 14 Sep 2024 10:57:45 +0800 Subject: [PATCH 22/46] feat: Add markers by API after map created --- src/dumbymap.mjs | 36 +++++++++++++++++------------------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/src/dumbymap.mjs b/src/dumbymap.mjs index a34998f..8222c80 100644 --- a/src/dumbymap.mjs +++ b/src/dumbymap.mjs @@ -217,9 +217,6 @@ export const generateMaps = async (container) => { // Render Maps {{{ const afterEachMapLoaded = (mapContainer) => { - mapContainer.querySelectorAll('.marker') - .forEach(marker => htmlHolder.anchors.push(marker)) - const focusClickedMap = () => { if (container.getAttribute('data-layout') !== 'none') return @@ -244,16 +241,9 @@ export const generateMaps = async (container) => { config.id = mapId } mapIdList.push(mapId) + return config } - // FIXME Create markers after maps are created - const markerOptions = geoLinks.map(link => ({ - targets: link.targets, - xy: link.xy, - title: link.url.pathname - })) - - // Render each code block with "language-map" class const renderTargets = Array.from(container.querySelectorAll('pre:has(.language-map)')) const renderAllTargets = renderTargets.map(async (target) => { @@ -266,14 +256,7 @@ export const generateMaps = async (container) => { let configList = [] try { - configList = parseConfigsFromYaml(configText).map(result => { - assignMapId(result) - const markersFromLinks = markerOptions.filter(marker => - !marker.targets || marker.targets.includes(result.id) - ) - Object.assign(result, { markers: markersFromLinks }) - return result - }) + configList = parseConfigsFromYaml(configText).map(assignMapId) } catch (_) { console.warn('Fail to parse yaml config for element', target) } @@ -292,6 +275,21 @@ export const generateMaps = async (container) => { }) }) const renderInfo = await Promise.all(renderAllTargets).then(() => 'Finish Rendering') + const maps = htmlHolder.querySelectorAll('.map-container') ?? [] + Array.from(maps) + .forEach(ele => { + const markers = geoLinks + .filter(link => !link.targets || link.targets.include(ele.id)) + .map(link => ({ + xy: link.xy, + title: link.url.pathname + })) + ele?.renderer?.addMarkers(markers) + }) + + htmlHolder.querySelectorAll('.marker') + .forEach(marker => htmlHolder.anchors.push(marker)) + console.info(renderInfo) //}}} From 64f24ee5fb1a27b66de2575a8d4b31646776f54a Mon Sep 17 00:00:00 2001 From: Hsieh Chin Fan Date: Sat, 14 Sep 2024 19:28:46 +0800 Subject: [PATCH 23/46] feat: Switch layout by attribute --- index.html | 2 +- src/css/dumbymap.css | 2 +- src/css/index.css | 50 +++++++++++++++++++++++++++----------------- src/editor.mjs | 32 ++++++++++++++++------------ 4 files changed, 52 insertions(+), 34 deletions(-) diff --git a/index.html b/index.html index 196458e..b6cd5e6 100644 --- a/index.html +++ b/index.html @@ -21,7 +21,7 @@ - +
diff --git a/src/css/dumbymap.css b/src/css/dumbymap.css index ba074a2..1dc7a09 100644 --- a/src/css/dumbymap.css +++ b/src/css/dumbymap.css @@ -106,7 +106,7 @@ } .DumbyMap[data-layout]:not([data-layout=none]) { - margin: 0; + margin: 0 auto; height: 100vh; width: 100%; diff --git a/src/css/index.css b/src/css/index.css index 9e75e93..17a8dd1 100644 --- a/src/css/index.css +++ b/src/css/index.css @@ -4,38 +4,50 @@ } body { - display: flex; - justify-items: stretch; width: 100%; -} + height: 100vh; + + &[data-layout=editing] { + display: flex; + align-items: stretch; + gap: 0.5em; + + padding: 0.5em; + + .DumbyMap { + order: 2; + flex: 1 0 calc(50% - 0.5em); + height: 100%; + overflow-y: scroll; + border: var(--content-border); + border-radius: var(--content-border-radius); + } + .editor { + display: block; -.DumbyMap { - order: 2; - flex: 0 0 49%; - height: calc(100vh - 15px); - margin: 10px; - overflow-y: scroll; - border: var(--content-border); - border-radius: var(--content-border-radius); + order: 1; + flex: 1 0 50%; + max-width: calc(50% - 0.5em); + height: 100%; + } + } } .editor { - order: 1; - flex: 0 0 48%; - max-width: 50vw; - height: calc(100vh - 15px); - margin: 10px; + display: none; } .EasyMDEContainer { display: flex; + align-items: stretch; + gap: 0.5em; + height: 100%; flex-direction: column; - height: calc(100vh - 20px); box-sizing: border-box; .CodeMirror { order: 1; - flex-grow: 1; + flex: 1 0 auto; border: var(--content-border); border-radius: var(--content-border-radius); padding-inline: 0; @@ -53,7 +65,7 @@ body { .editor-toolbar { order: 2; - margin-top: 0.5rem; + flex: 0 0 auto; border-left: 1px solid #ced4da; border-right: 1px solid #ced4da; border-bottom: 1px solid #ced4da; diff --git a/src/editor.mjs b/src/editor.mjs index 915ce24..b88e488 100644 --- a/src/editor.mjs +++ b/src/editor.mjs @@ -9,15 +9,13 @@ import { createDocLinks } from './dumbymap.mjs' const HtmlContainer = document.querySelector(".DumbyMap") const textArea = document.querySelector(".editor textarea") -const toggleMaps = (container) => { - if (!container.querySelector('.Showcase')) { - generateMaps(container) - document.activeElement.blur(); +const toggleEditing = () => { + if (document.body.getAttribute("data-layout") === "editing") { + document.body.removeAttribute("data-layout") } else { - markdown2HTML(HtmlContainer, editor.value()) - createDocLinks(container) - container.setAttribute('data-layout', 'none') + document.body.setAttribute("data-layout", "editing") } + HtmlContainer.setAttribute("data-layout", "none") } // }}} // Set up EasyMDE {{{ @@ -66,7 +64,7 @@ const editor = new EasyMDE({ name: 'map', title: 'Toggle Map Generation', text: "🌏", - action: () => toggleMaps(HtmlContainer), + action: () => toggleEditing(), }, { name: 'debug', @@ -88,10 +86,6 @@ const cm = editor.codemirror markdown2HTML(HtmlContainer, editor.value()) createDocLinks(HtmlContainer) -if (queryParams.get('render')) { - toggleMaps(HtmlContainer) -} - // Quick hack to style lines inside code block const addClassToCodeLines = () => { const lines = cm.getLineHandle(0).parent.lines @@ -509,11 +503,23 @@ cm.on('keydown', (_, e) => { document.onkeydown = (e) => { if (e.altKey && e.ctrlKey && e.key === 'm') { - toggleMaps(HtmlContainer) + toggleEditing() } } // }}} // }}} +const layoutObserver = new MutationObserver(() => { + const layout = HtmlContainer.getAttribute('data-layout') + if (layout !== 'none') { + document.body.removeAttribute('data-layout') + } +}) + +layoutObserver.observe(HtmlContainer, { + attributes: true, + attributeFilter: ["data-layout"], + attributeOldValue: true +}); // vim: sw=2 ts=2 foldmethod=marker foldmarker={{{,}}} From ae182b739a5aec6f2262891cb0d3e5bb85a1fe26 Mon Sep 17 00:00:00 2001 From: Hsieh Chin Fan Date: Sat, 14 Sep 2024 19:30:08 +0800 Subject: [PATCH 24/46] feat: Set maps full-width by default Use new API "renderWith" with converter --- package.json | 2 +- src/dumbymap.mjs | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index e66a45b..2e033e0 100644 --- a/package.json +++ b/package.json @@ -45,7 +45,7 @@ "dependencies": { "easymde": "^2.18.0", "leader-line": "^1.0.7", - "mapclay": "^0.6.2", + "mapclay": "^0.6.3", "markdown-it": "^14.1.0", "markdown-it-anchor": "^9.2.0", "markdown-it-footnote": "^4.0.0", diff --git a/src/dumbymap.mjs b/src/dumbymap.mjs index 8222c80..f9a999b 100644 --- a/src/dumbymap.mjs +++ b/src/dumbymap.mjs @@ -5,7 +5,7 @@ import MarkdownItFrontMatter from 'markdown-it-front-matter' import MarkdownItTocDoneRight from 'markdown-it-toc-done-right' import LeaderLine from 'leader-line' import PlainDraggable from 'plain-draggable' -import { render, parseConfigsFromYaml } from 'mapclay' +import { renderWith, parseConfigsFromYaml } from 'mapclay' // Utils {{{ const onRemove = (element, callback) => { @@ -245,6 +245,7 @@ export const generateMaps = async (container) => { } // Render each code block with "language-map" class + const render = renderWith(config => ({ width: "100%", ...config })) const renderTargets = Array.from(container.querySelectorAll('pre:has(.language-map)')) const renderAllTargets = renderTargets.map(async (target) => { // Get text in code block starts with '```map' From 8aadc8fd3b69fbd8a030e88426816c38ca32f4af Mon Sep 17 00:00:00 2001 From: Hsieh Chin Fan Date: Sat, 14 Sep 2024 19:31:23 +0800 Subject: [PATCH 25/46] feat: Render maps directly be debounce --- src/editor.mjs | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/src/editor.mjs b/src/editor.mjs index b88e488..b93f9ba 100644 --- a/src/editor.mjs +++ b/src/editor.mjs @@ -139,10 +139,30 @@ const completeForCodeBlock = (change) => { } } -// Re-render HTML by editor content -cm.on("change", (_, change) => { +const debounceForMap = (() => { + let timer = null; + + return function(...args) { + let context = this; + + clearTimeout(timer); + timer = setTimeout(() => { + generateMaps.apply(context, args) + }, 1000); + } +})() + +const updateDumbyMap = () => { markdown2HTML(HtmlContainer, editor.value()) createDocLinks(HtmlContainer) + debounceForMap(HtmlContainer) +} + +updateDumbyMap() + +// Re-render HTML by editor content +cm.on("change", (_, change) => { + updateDumbyMap() addClassToCodeLines() completeForCodeBlock(change) }) From e25db413c595623b18315bd716ba7171f32ccc51 Mon Sep 17 00:00:00 2001 From: Hsieh Chin Fan Date: Sun, 15 Sep 2024 00:16:02 +0800 Subject: [PATCH 26/46] feat: Improve draggable behavior in overlay layout * Set position=absolute on each block, so they won't affect each others' y value when display=none (about Block Formatting Context) * Add title tooltip for removing block * Simple logic to set initial position of block by their width --- src/css/dumbymap.css | 1 + src/dumbymap.mjs | 60 ++++++++++++++++++++++++++++---------------- 2 files changed, 40 insertions(+), 21 deletions(-) diff --git a/src/css/dumbymap.css b/src/css/dumbymap.css index 1dc7a09..8e28af5 100644 --- a/src/css/dumbymap.css +++ b/src/css/dumbymap.css @@ -142,6 +142,7 @@ > .draggable-block { box-sizing: content-box; + position: absolute; width: fit-content; max-height: 50vh; overflow: scroll; diff --git a/src/dumbymap.mjs b/src/dumbymap.mjs index f9a999b..4d4628d 100644 --- a/src/dumbymap.mjs +++ b/src/dumbymap.mjs @@ -294,6 +294,26 @@ export const generateMaps = async (container) => { console.info(renderInfo) //}}} + // Draggable Blocks{{{ + // Add draggable part for blocks + htmlHolder.blocks = Array.from(htmlHolder.querySelectorAll('.draggable-block')) + htmlHolder.blocks.forEach(block => { + // Add draggable part + const draggablePart = document.createElement('div'); + draggablePart.classList.add('draggable') + draggablePart.textContent = '☰' + draggablePart.title = 'Use middle-click to remove block' + block.insertBefore(draggablePart, block.firstChild) + block.draggablePart = draggablePart + + draggablePart.onmouseup = (e) => { + if (e.button === 1) { + block.style.display = "none"; + } + } + }) + + // }}} // CSS observer {{{ // Set focusArea @@ -323,20 +343,6 @@ export const generateMaps = async (container) => { } } - // Add draggable part for blocks - htmlHolder.blocks = Array.from(htmlHolder.querySelectorAll('.draggable-block')) - htmlHolder.blocks.forEach(block => { - const draggablePart = document.createElement('div'); - draggablePart.classList.add('draggable') - draggablePart.textContent = '☰' - - // TODO Better way to close block - draggablePart.onmouseup = (e) => { - if (e.button === 1) block.style.display = "none"; - } - block.insertBefore(draggablePart, block.firstChild) - }) - // observe layout change const layoutObserver = new MutationObserver(() => { const layout = container.getAttribute('data-layout') @@ -363,17 +369,29 @@ export const generateMaps = async (container) => { } if (layout === 'overlay') { - htmlHolder.blocks.forEach(block => { - block.draggableInstance = new PlainDraggable(block, { handle: block.querySelector('.draggable') }) - block.draggableInstance.snap = { x: { step: 20 }, y: { step: 20 } } - // block.draggableInstance.onDragEnd = () => { - // links(block).forEach(link => link.line.position()) - // } + let x = 0; + let y = 0; + htmlHolder.blocks.forEach(block =>{ + block.draggableInstance = new PlainDraggable(block, { + handle: block.draggablePart, + snap: { x: { step: 20 }, y: { step: 20 } }, + autoScroll: false, + }) + // block.style.transform = `translate(${x}px, ${y}px)` + block.style.left = `${x}px` + block.style.top = `${y}px` + x += parseInt(window.getComputedStyle(block).width) + 50 + if (x > window.innerWidth) { + y += 200 + x = x % window.innerWidth + } }) } else { htmlHolder.blocks.forEach(block => { block.style.transform = 'none' - block.draggableInstance?.remove() + try { + block.draggableInstance?.remove() + } catch (err) { } }) } }); From 7bfa3fdd32f5fb037ebec24b185c7f1e4661c46a Mon Sep 17 00:00:00 2001 From: Hsieh Chin Fan Date: Sun, 15 Sep 2024 00:24:36 +0800 Subject: [PATCH 27/46] fix(CSS): z-index of blocks as overlay --- src/css/dumbymap.css | 1 + 1 file changed, 1 insertion(+) diff --git a/src/css/dumbymap.css b/src/css/dumbymap.css index 8e28af5..a41cd57 100644 --- a/src/css/dumbymap.css +++ b/src/css/dumbymap.css @@ -139,6 +139,7 @@ .SemanticHtml { font-size: 12px; pointer-events: none; + z-index: 1; > .draggable-block { box-sizing: content-box; From dfc9ccff45df96d0ffbfef71bbec0a7233b8f391 Mon Sep 17 00:00:00 2001 From: Hsieh Chin Fan Date: Sun, 15 Sep 2024 00:25:25 +0800 Subject: [PATCH 28/46] chore(CSS): Improve layout of DumbyMap --- src/css/dumbymap.css | 15 +++++++++++---- src/css/index.css | 4 ++-- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/css/dumbymap.css b/src/css/dumbymap.css index a41cd57..e9d9dba 100644 --- a/src/css/dumbymap.css +++ b/src/css/dumbymap.css @@ -39,23 +39,21 @@ /* height: 100vh; */ .Showcase { - flex: 0; + order: 2; #mapPlaceholder { display: none; } } .SemanticHtml { - flex: 1; + order: 1; padding: 0.5rem; overflow-y: scroll; > .draggable-block { width: 100%; - position: relative; pointer-events: auto; border-radius: 0.5rem; - padding: 0.5rem; background-color: white; margin-bottom: 3rem; @@ -145,14 +143,22 @@ box-sizing: content-box; position: absolute; width: fit-content; + height: fill-available; max-height: 50vh; overflow: scroll; border: solid gray; + padding: 0.3rem; + padding-top: 0.5rem; + padding-bottom: 0; * { max-width: calc(100vw/4); } + .map-container { + min-width: 200px; + } + .draggable { width: 100%; display: block; @@ -167,6 +173,7 @@ transition: all 0.3s ease-out; &:hover { background-color: lightgray; + padding-top: 1em; } } } diff --git a/src/css/index.css b/src/css/index.css index 17a8dd1..49b3abb 100644 --- a/src/css/index.css +++ b/src/css/index.css @@ -15,7 +15,7 @@ body { padding: 0.5em; .DumbyMap { - order: 2; + order: 1; flex: 1 0 calc(50% - 0.5em); height: 100%; overflow-y: scroll; @@ -25,7 +25,7 @@ body { .editor { display: block; - order: 1; + order: 2; flex: 1 0 50%; max-width: calc(50% - 0.5em); height: 100%; From 19aa38f4c29ea2349207a25292d1970ce596b3f1 Mon Sep 17 00:00:00 2001 From: Hsieh Chin Fan Date: Sun, 15 Sep 2024 00:25:51 +0800 Subject: [PATCH 29/46] style: Reformat code --- src/editor.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/editor.mjs b/src/editor.mjs index b93f9ba..67f0d6a 100644 --- a/src/editor.mjs +++ b/src/editor.mjs @@ -227,7 +227,7 @@ const insideCodeblockForMap = (anchor) => { const content = cm.getLine(line) if (content === '```map') { return true - } else if (content === '```'){ + } else if (content === '```') { return false } line = line - 1 From 0a081ceddbc10212c87c337b054b0ca4f3534666 Mon Sep 17 00:00:00 2001 From: Hsieh Chin Fan Date: Sun, 15 Sep 2024 11:00:41 +0800 Subject: [PATCH 30/46] fix(CSS): Set html content full width --- src/css/dumbymap.css | 3 ++- src/css/index.css | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/css/dumbymap.css b/src/css/dumbymap.css index e9d9dba..ab09863 100644 --- a/src/css/dumbymap.css +++ b/src/css/dumbymap.css @@ -47,8 +47,9 @@ .SemanticHtml { order: 1; - padding: 0.5rem; + padding: 1.5rem; overflow-y: scroll; + width: 100%; > .draggable-block { width: 100%; diff --git a/src/css/index.css b/src/css/index.css index 49b3abb..b8c8ba5 100644 --- a/src/css/index.css +++ b/src/css/index.css @@ -47,7 +47,7 @@ body { .CodeMirror { order: 1; - flex: 1 0 auto; + flex: 1 0 0; border: var(--content-border); border-radius: var(--content-border-radius); padding-inline: 0; @@ -65,7 +65,7 @@ body { .editor-toolbar { order: 2; - flex: 0 0 auto; + flex: 0 0 0; border-left: 1px solid #ced4da; border-right: 1px solid #ced4da; border-bottom: 1px solid #ced4da; From c224ea482e4a89cfa4573a7d0748616e81e1924b Mon Sep 17 00:00:00 2001 From: Hsieh Chin Fan Date: Sun, 15 Sep 2024 11:01:19 +0800 Subject: [PATCH 31/46] chore: eslint --- src/css/dumbymap.css | 1 - src/dumbymap.mjs | 2 +- src/editor.mjs | 5 +---- 3 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/css/dumbymap.css b/src/css/dumbymap.css index ab09863..2da2376 100644 --- a/src/css/dumbymap.css +++ b/src/css/dumbymap.css @@ -144,7 +144,6 @@ box-sizing: content-box; position: absolute; width: fit-content; - height: fill-available; max-height: 50vh; overflow: scroll; border: solid gray; diff --git a/src/dumbymap.mjs b/src/dumbymap.mjs index 4d4628d..ba0ef0c 100644 --- a/src/dumbymap.mjs +++ b/src/dumbymap.mjs @@ -391,7 +391,7 @@ export const generateMaps = async (container) => { block.style.transform = 'none' try { block.draggableInstance?.remove() - } catch (err) { } + } catch (_) { } }) } }); diff --git a/src/editor.mjs b/src/editor.mjs index 67f0d6a..14daab4 100644 --- a/src/editor.mjs +++ b/src/editor.mjs @@ -34,7 +34,6 @@ const getContentFromHash = (hash) => { } const initialState = getStateFromHash(window.location.hash) -const queryParams = new URL(window.location).searchParams window.location.hash = '' const contentFromHash = initialState.content const lastContent = localStorage.getItem('editorContent') @@ -143,11 +142,9 @@ const debounceForMap = (() => { let timer = null; return function(...args) { - let context = this; - clearTimeout(timer); timer = setTimeout(() => { - generateMaps.apply(context, args) + generateMaps.apply(this, args) }, 1000); } })() From 11e10393fc5e1c194bc6668f156eb51673a2b011 Mon Sep 17 00:00:00 2001 From: Hsieh Chin Fan Date: Sun, 15 Sep 2024 11:01:49 +0800 Subject: [PATCH 32/46] feat(markdown): Set line break valid by default --- src/dumbymap.mjs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/dumbymap.mjs b/src/dumbymap.mjs index ba0ef0c..e19e2d9 100644 --- a/src/dumbymap.mjs +++ b/src/dumbymap.mjs @@ -92,7 +92,10 @@ export const markdown2HTML = (container, mdContent) => { container.innerHTML = '
' const htmlHolder = container.querySelector('.SemanticHtml') - const md = MarkdownIt({ html: true }) + const md = MarkdownIt({ + html: true, + breaks: true, + }) .use(MarkdownItAnchor, { permalink: MarkdownItAnchor.permalink.linkInsideHeader({ placement: 'before' }) }) From 1c5a950c274a7acd9f98a3ac68ad2426c16eb097 Mon Sep 17 00:00:00 2001 From: Hsieh Chin Fan Date: Sun, 15 Sep 2024 11:04:09 +0800 Subject: [PATCH 33/46] fix: Remove soft link by mistake --- .gitignore | 2 +- mapclay | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) delete mode 120000 mapclay diff --git a/.gitignore b/.gitignore index 8b4a843..3e11007 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,4 @@ dist/ node_modules/ package-lock.json pnpm-lock.yaml -mapclay/ +mapclay diff --git a/mapclay b/mapclay deleted file mode 120000 index 053dbdf..0000000 --- a/mapclay +++ /dev/null @@ -1 +0,0 @@ -/home/pham/git/mapclay \ No newline at end of file From 7a3cbaf8bb8fdbae487f96a81b881d42f732f6b5 Mon Sep 17 00:00:00 2001 From: Hsieh Chin Fan Date: Sun, 15 Sep 2024 11:51:30 +0800 Subject: [PATCH 34/46] feat(CSS): Make draggable blocks resizable --- src/css/dumbymap.css | 8 ++++++-- src/dumbymap.mjs | 7 ++++--- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/css/dumbymap.css b/src/css/dumbymap.css index 2da2376..876d980 100644 --- a/src/css/dumbymap.css +++ b/src/css/dumbymap.css @@ -145,14 +145,18 @@ position: absolute; width: fit-content; max-height: 50vh; + max-width: 25vw; overflow: scroll; border: solid gray; padding: 0.3rem; padding-top: 0.5rem; padding-bottom: 0; + resize: both; - * { - max-width: calc(100vw/4); + &[style*="height"], + &[style*="width"] { + max-height: unset; + max-width: unset; } .map-container { diff --git a/src/dumbymap.mjs b/src/dumbymap.mjs index e19e2d9..922f413 100644 --- a/src/dumbymap.mjs +++ b/src/dumbymap.mjs @@ -375,12 +375,13 @@ export const generateMaps = async (container) => { let x = 0; let y = 0; htmlHolder.blocks.forEach(block =>{ + // Add draggable instance block.draggableInstance = new PlainDraggable(block, { handle: block.draggablePart, snap: { x: { step: 20 }, y: { step: 20 } }, autoScroll: false, }) - // block.style.transform = `translate(${x}px, ${y}px)` + // Set initial postion side by side block.style.left = `${x}px` block.style.top = `${y}px` x += parseInt(window.getComputedStyle(block).width) + 50 @@ -391,9 +392,9 @@ export const generateMaps = async (container) => { }) } else { htmlHolder.blocks.forEach(block => { - block.style.transform = 'none' + block.removeAttribute('style') try { - block.draggableInstance?.remove() + block.draggableInstance.remove() } catch (_) { } }) } From 835819f443baecd2220db9bb8a80fb0e43bede3d Mon Sep 17 00:00:00 2001 From: Hsieh Chin Fan Date: Sun, 15 Sep 2024 12:43:20 +0800 Subject: [PATCH 35/46] feat(CSS): Set padding for each element in draggable block --- src/css/dumbymap.css | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/css/dumbymap.css b/src/css/dumbymap.css index 876d980..bbbd38c 100644 --- a/src/css/dumbymap.css +++ b/src/css/dumbymap.css @@ -148,11 +148,14 @@ max-width: 25vw; overflow: scroll; border: solid gray; - padding: 0.3rem; padding-top: 0.5rem; padding-bottom: 0; resize: both; + * { + padding-inline: 0.5rem; + } + &[style*="height"], &[style*="width"] { max-height: unset; @@ -161,23 +164,24 @@ .map-container { min-width: 200px; + padding-inline: 0; } .draggable { - width: 100%; display: block; top: 0; - left: 0; position: sticky; + background-color: white; margin-bottom: -18px; - opacity: 0.3; transform: translate(0, -16px); + width: 100%; + padding-inline: 0; text-align: center; z-index: 100; transition: all 0.3s ease-out; &:hover { - background-color: lightgray; - padding-top: 1em; + background-color: #f1f1f1; + padding-block: 1em 0.5em; } } } From d02dafa0b0c1717aa73305723d4edaf0f0b3316e Mon Sep 17 00:00:00 2001 From: Hsieh Chin Fan Date: Sun, 15 Sep 2024 15:12:23 +0800 Subject: [PATCH 36/46] chore: patch eefd82a Remove scroll event made by plain-draggable in build stage This change prevents inline style of draggable element got changed with ANY scroll event. (Especially width/height set by resize event) Ref: https://github.com/anseki/plain-draggable/issues/124 --- rollup.config.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/rollup.config.js b/rollup.config.js index d04eb4e..78a4123 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -28,6 +28,16 @@ const general = { return null; }, }, + { + name: 'plain-draggable', + transform(code, id) { + if (id.includes('node_modules/plain-draggable/')) { + const removePattern = /window\.addEventListener\('scroll'[^\)]*\)/ + return `${code.replace(removePattern, "")}`; + } + return null; + }, + }, { name: 'mapclay', resolveId(source) { From 6e5be261137024946d75c0e0451b2e9eb1c39f02 Mon Sep 17 00:00:00 2001 From: Hsieh Chin Fan Date: Sun, 15 Sep 2024 15:17:11 +0800 Subject: [PATCH 37/46] chore: eslint --- src/dumbymap.mjs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/dumbymap.mjs b/src/dumbymap.mjs index 922f413..d8349c6 100644 --- a/src/dumbymap.mjs +++ b/src/dumbymap.mjs @@ -374,7 +374,7 @@ export const generateMaps = async (container) => { if (layout === 'overlay') { let x = 0; let y = 0; - htmlHolder.blocks.forEach(block =>{ + htmlHolder.blocks.forEach(block => { // Add draggable instance block.draggableInstance = new PlainDraggable(block, { handle: block.draggablePart, @@ -395,7 +395,7 @@ export const generateMaps = async (container) => { block.removeAttribute('style') try { block.draggableInstance.remove() - } catch (_) { } + } catch (_) { null } }) } }); From 58f52c95031bf0873c58ccf380b5e80a7701fdfa Mon Sep 17 00:00:00 2001 From: Hsieh Chin Fan Date: Sun, 15 Sep 2024 15:18:00 +0800 Subject: [PATCH 38/46] chore(release): 0.2.4 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 2e033e0..c2a5150 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "dumbymap", "description": "Generate interactive maps from Semantic HTML", - "version": "0.2.3", + "version": "0.2.4", "license": "MIT", "type": "module", "main": "dist/dumbymap.mjs", From 9d956f6e85e7dd89a72b2ca076e1acaa6d8c0130 Mon Sep 17 00:00:00 2001 From: Hsieh Chin Fan Date: Sun, 15 Sep 2024 15:45:32 +0800 Subject: [PATCH 39/46] feat(CSS): Make toolbar has general border style --- src/css/index.css | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/css/index.css b/src/css/index.css index b8c8ba5..c754600 100644 --- a/src/css/index.css +++ b/src/css/index.css @@ -66,11 +66,9 @@ body { .editor-toolbar { order: 2; flex: 0 0 0; - border-left: 1px solid #ced4da; - border-right: 1px solid #ced4da; - border-bottom: 1px solid #ced4da; - border-bottom-left-radius: 4px; - border-bottom-right-radius: 4px; + + border: var(--content-border); + border-radius: var(--content-border-radius); } .editor-statusbar { From 2b42596d8416042d0014cf3603be7e28ede1faa8 Mon Sep 17 00:00:00 2001 From: Hsieh Chin Fan Date: Sun, 15 Sep 2024 15:49:23 +0800 Subject: [PATCH 40/46] fix: Use correct way to set initial position of draggable blocks Otherwise, inline style 'transform' would be set to 0 when first-time dragging, 'left' and 'top' would affect draggable area. --- src/dumbymap.mjs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/dumbymap.mjs b/src/dumbymap.mjs index d8349c6..47c629c 100644 --- a/src/dumbymap.mjs +++ b/src/dumbymap.mjs @@ -372,18 +372,17 @@ export const generateMaps = async (container) => { } if (layout === 'overlay') { - let x = 0; - let y = 0; + let [x, y] = [0, 0]; htmlHolder.blocks.forEach(block => { // Add draggable instance block.draggableInstance = new PlainDraggable(block, { handle: block.draggablePart, snap: { x: { step: 20 }, y: { step: 20 } }, - autoScroll: false, + left: x, + top: y, }) + // Set initial postion side by side - block.style.left = `${x}px` - block.style.top = `${y}px` x += parseInt(window.getComputedStyle(block).width) + 50 if (x > window.innerWidth) { y += 200 From eb468ad16b0bf94eb37ecfdd2949adf73922b7dd Mon Sep 17 00:00:00 2001 From: Hsieh Chin Fan Date: Sun, 15 Sep 2024 18:10:30 +0800 Subject: [PATCH 41/46] feat: Add arg or callback for each map rendered --- src/dumbymap.mjs | 88 +++++++++++++++++++++++++----------------------- 1 file changed, 46 insertions(+), 42 deletions(-) diff --git a/src/dumbymap.mjs b/src/dumbymap.mjs index 47c629c..72e864e 100644 --- a/src/dumbymap.mjs +++ b/src/dumbymap.mjs @@ -138,7 +138,7 @@ export const markdown2HTML = (container, mdContent) => { //}}} } // FIXME Don't use hard-coded CSS selector -export const generateMaps = async (container) => { +export const generateMaps = async (container, callback) => { // LeaderLine {{{ // Get anchors with "geo:" scheme @@ -250,51 +250,55 @@ export const generateMaps = async (container) => { // Render each code block with "language-map" class const render = renderWith(config => ({ width: "100%", ...config })) const renderTargets = Array.from(container.querySelectorAll('pre:has(.language-map)')) - const renderAllTargets = renderTargets.map(async (target) => { - // Get text in code block starts with '```map' - const configText = target.querySelector('.language-map') - .textContent - // BE CAREFUL!!! 0xa0 char is "non-breaking spaces" in HTML text content - // replace it by normal space - .replace(/\u00A0/g, '\u0020') - - let configList = [] - try { - configList = parseConfigsFromYaml(configText).map(assignMapId) - } catch (_) { - console.warn('Fail to parse yaml config for element', target) - } + .map(async (target) => { + // Get text in code block starts with '```map' + const configText = target.querySelector('.language-map') + .textContent + // BE CAREFUL!!! 0xa0 char is "non-breaking spaces" in HTML text content + // replace it by normal space + .replace(/\u00A0/g, '\u0020') + + let configList = [] + try { + configList = parseConfigsFromYaml(configText).map(assignMapId) + } catch (_) { + console.warn('Fail to parse yaml config for element', target) + } - // Render maps - return render(target, configList) - .then(results => { - results.forEach((mapByConfig) => { - if (mapByConfig.status === 'fulfilled') { - afterEachMapLoaded(mapByConfig.value) - return mapByConfig.value - } else { - console.error('Fail to render target element', mapByConfig.reason) - } + // Render maps + return render(target, configList) + .then(results => { + results.forEach((mapByConfig) => { + if (mapByConfig.status === 'fulfilled') { + afterEachMapLoaded(mapByConfig.value) + return mapByConfig.value + } else { + console.error('Fail to render target element', mapByConfig.reason) + } + }) }) - }) - }) - const renderInfo = await Promise.all(renderAllTargets).then(() => 'Finish Rendering') - const maps = htmlHolder.querySelectorAll('.map-container') ?? [] - Array.from(maps) - .forEach(ele => { - const markers = geoLinks - .filter(link => !link.targets || link.targets.include(ele.id)) - .map(link => ({ - xy: link.xy, - title: link.url.pathname - })) - ele?.renderer?.addMarkers(markers) }) - htmlHolder.querySelectorAll('.marker') - .forEach(marker => htmlHolder.anchors.push(marker)) + const renderAllTargets = Promise.all(renderTargets) + renderAllTargets.then(() => { + console.info('Finish Rendering') + + const maps = htmlHolder.querySelectorAll('.map-container') ?? [] + Array.from(maps) + .forEach(ele => { + callback(ele) + const markers = geoLinks + .filter(link => !link.targets || link.targets.include(ele.id)) + .map(link => ({ + xy: link.xy, + title: link.url.pathname + })) + ele?.renderer?.addMarkers(markers) + }) - console.info(renderInfo) + htmlHolder.querySelectorAll('.marker') + .forEach(marker => htmlHolder.anchors.push(marker)) + }) //}}} // Draggable Blocks{{{ @@ -407,5 +411,5 @@ export const generateMaps = async (container) => { onRemove(htmlHolder, () => layoutObserver.disconnect()) //}}} //}}} - return container + return renderAllTargets } From 02aba12a3215efbd496ac667924da5937b59283d Mon Sep 17 00:00:00 2001 From: Hsieh Chin Fan Date: Sun, 15 Sep 2024 18:10:55 +0800 Subject: [PATCH 42/46] feat: Set callback for click event --- src/editor.mjs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/editor.mjs b/src/editor.mjs index 14daab4..8ab2389 100644 --- a/src/editor.mjs +++ b/src/editor.mjs @@ -149,10 +149,18 @@ const debounceForMap = (() => { } })() +const afterMapRendered = (mapHolder) => { + mapHolder.oncontextmenu = (event) => { + event.preventDefault() + const lonLat = mapHolder.renderer.unproject([event.x, event.y]) + // TODO... + } +} + const updateDumbyMap = () => { markdown2HTML(HtmlContainer, editor.value()) createDocLinks(HtmlContainer) - debounceForMap(HtmlContainer) + debounceForMap(HtmlContainer, afterMapRendered) } updateDumbyMap() From 4ae512fe8394b87c722377917332895a756a575a Mon Sep 17 00:00:00 2001 From: Hsieh Chin Fan Date: Mon, 16 Sep 2024 00:31:02 +0800 Subject: [PATCH 43/46] feat(CSS): draggable handle * Linear gradient for scroll-up contents * Set higer z-index to overlap elments like leaflets * Gray-out contents when interacting with handle * Set content padding only for direct children for block --- src/css/dumbymap.css | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/css/dumbymap.css b/src/css/dumbymap.css index bbbd38c..0e24785 100644 --- a/src/css/dumbymap.css +++ b/src/css/dumbymap.css @@ -152,7 +152,7 @@ padding-bottom: 0; resize: both; - * { + > * { padding-inline: 0.5rem; } @@ -171,17 +171,22 @@ display: block; top: 0; position: sticky; - background-color: white; + background: linear-gradient(0deg, rgba(255,255,255,0), rgba(255,255,255,1) 60%); margin-bottom: -18px; transform: translate(0, -16px); width: 100%; padding-inline: 0; + padding-bottom: 2em; text-align: center; - z-index: 100; + z-index: 9999; transition: all 0.3s ease-out; &:hover { - background-color: #f1f1f1; + background: #e1e1e1; padding-block: 1em 0.5em; + & ~ * { + opacity: 0.7; + color: gray; + } } } } From a7a3243f37765b2ac3d4d3de89e4d81cb7935c47 Mon Sep 17 00:00:00 2001 From: Hsieh Chin Fan Date: Mon, 16 Sep 2024 00:47:15 +0800 Subject: [PATCH 44/46] fix: method call about geoLink --- src/dumbymap.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dumbymap.mjs b/src/dumbymap.mjs index 72e864e..6b64ee4 100644 --- a/src/dumbymap.mjs +++ b/src/dumbymap.mjs @@ -288,7 +288,7 @@ export const generateMaps = async (container, callback) => { .forEach(ele => { callback(ele) const markers = geoLinks - .filter(link => !link.targets || link.targets.include(ele.id)) + .filter(link => !link.targets || link.targets.includes(ele.id)) .map(link => ({ xy: link.xy, title: link.url.pathname From f04a55d7716f549c0c3747bfbf47d2d2b12efe6c Mon Sep 17 00:00:00 2001 From: Hsieh Chin Fan Date: Mon, 16 Sep 2024 17:16:43 +0800 Subject: [PATCH 45/46] fix(CSS): stick draggable handle by scroll --- src/css/dumbymap.css | 1 + 1 file changed, 1 insertion(+) diff --git a/src/css/dumbymap.css b/src/css/dumbymap.css index 0e24785..6554ceb 100644 --- a/src/css/dumbymap.css +++ b/src/css/dumbymap.css @@ -170,6 +170,7 @@ .draggable { display: block; top: 0; + left: 0; position: sticky; background: linear-gradient(0deg, rgba(255,255,255,0), rgba(255,255,255,1) 60%); margin-bottom: -18px; From be3c3f74fe0f782d71aae89759cf6954f25cd156 Mon Sep 17 00:00:00 2001 From: Hsieh Chin Fan Date: Mon, 16 Sep 2024 18:47:26 +0800 Subject: [PATCH 46/46] chore(release): 0.2.5 --- package.json | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index c2a5150..487c242 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "dumbymap", "description": "Generate interactive maps from Semantic HTML", - "version": "0.2.4", + "version": "0.2.5", "license": "MIT", "type": "module", "main": "dist/dumbymap.mjs", @@ -17,7 +17,6 @@ "openlayers", "leaflet" ], - "license": "MIT", "scripts": { "watch": "npx rollup -c -w", "build": "mkdir -p dist; npm run build-css; npm run build-renderers; npm run build-resources; npx rollup -c", @@ -45,7 +44,7 @@ "dependencies": { "easymde": "^2.18.0", "leader-line": "^1.0.7", - "mapclay": "^0.6.3", + "mapclay": "^0.6.4", "markdown-it": "^14.1.0", "markdown-it-anchor": "^9.2.0", "markdown-it-footnote": "^4.0.0",