diff options
author | Hsieh Chin Fan <pham@topo.tw> | 2024-10-16 16:57:47 +0800 |
---|---|---|
committer | Hsieh Chin Fan <pham@topo.tw> | 2024-10-16 18:57:18 +0800 |
commit | 1caf56dc0a2bbf4bb484fe4f312b5229a63aace3 (patch) | |
tree | 913a3e5803e9d4128d0ad6d8bd6ed64326d5e5d4 | |
parent | 9c90bd1cbdc5de5b50def0eb4cb3e65e80194af3 (diff) |
feat: add simple script to test mapclay
* add more options for generateMaps for general purposes
* add rollup config for adding script into addon
-rw-r--r-- | addon/index.mjs | 17 | ||||
-rw-r--r-- | addon/manifest.json | 10 | ||||
-rw-r--r-- | scripts/rollup.config.js | 55 | ||||
-rw-r--r-- | src/dumbymap.mjs | 128 |
4 files changed, 153 insertions, 57 deletions
diff --git a/addon/index.mjs b/addon/index.mjs new file mode 100644 index 0000000..323edf1 --- /dev/null +++ b/addon/index.mjs | |||
@@ -0,0 +1,17 @@ | |||
1 | const { Leaflet } = window.mapclay.renderers | ||
2 | const simpleRender = window.mapclay.renderWith(config => ({ | ||
3 | use: 'Leaflet', | ||
4 | width: '100%', | ||
5 | height: '200px', | ||
6 | XYZ: 'https://tile.openstreetmap.jp/styles/osm-bright/512/{z}/{x}/{y}.png', | ||
7 | ...config, | ||
8 | aliases: { | ||
9 | use: { Leaflet }, | ||
10 | ...(config.aliases ?? {}), | ||
11 | }, | ||
12 | })) | ||
13 | |||
14 | window.generateMaps(document.querySelector('main') ?? document.body, { | ||
15 | initialLayout: '', | ||
16 | render: simpleRender, | ||
17 | }) | ||
diff --git a/addon/manifest.json b/addon/manifest.json index 27433b3..9cc6607 100644 --- a/addon/manifest.json +++ b/addon/manifest.json | |||
@@ -11,8 +11,14 @@ | |||
11 | 11 | ||
12 | "content_scripts": [ | 12 | "content_scripts": [ |
13 | { | 13 | { |
14 | "matches": ["*://*.mozilla.org/*"], | 14 | "matches": [ |
15 | "js": ["index.mjs"], | 15 | "*://*.mozilla.org/*", |
16 | "*://hackmd.io/*" | ||
17 | ], | ||
18 | "js": [ | ||
19 | "dumbymap.mjs", | ||
20 | "index.mjs" | ||
21 | ], | ||
16 | "css": [ | 22 | "css": [ |
17 | "css/dumbymap.css" | 23 | "css/dumbymap.css" |
18 | ] | 24 | ] |
diff --git a/scripts/rollup.config.js b/scripts/rollup.config.js index d4af05f..7dc9f75 100644 --- a/scripts/rollup.config.js +++ b/scripts/rollup.config.js | |||
@@ -5,7 +5,25 @@ import { existsSync } from 'fs' | |||
5 | import { join } from 'path' | 5 | import { join } from 'path' |
6 | import { bundleStats } from 'rollup-plugin-bundle-stats' | 6 | import { bundleStats } from 'rollup-plugin-bundle-stats' |
7 | 7 | ||
8 | const production = !process.env.ROLLUP_WATCH | 8 | const prod = process.env.PRODUCTION |
9 | const addon = process.env.ADDON | ||
10 | |||
11 | function resolve (file, origin) { | ||
12 | // Your way to resolve local include path | ||
13 | } | ||
14 | |||
15 | function pathResolve (options) { | ||
16 | return { | ||
17 | resolveId: function (file, origin) { | ||
18 | // Your local include path must either starts with `./` or `../` | ||
19 | if (file.startsWith('./') || file.startsWith('../')) { | ||
20 | // Return an absolute include path | ||
21 | return resolve(file, origin) | ||
22 | } | ||
23 | return null // Continue to the next plugins! | ||
24 | }, | ||
25 | } | ||
26 | } | ||
9 | 27 | ||
10 | const general = { | 28 | const general = { |
11 | output: [ | 29 | output: [ |
@@ -13,7 +31,6 @@ const general = { | |||
13 | dir: './dist', | 31 | dir: './dist', |
14 | format: 'esm', | 32 | format: 'esm', |
15 | entryFileNames: '[name].mjs', | 33 | entryFileNames: '[name].mjs', |
16 | sourcemap: 'true', | ||
17 | }, | 34 | }, |
18 | ], | 35 | ], |
19 | watch: { | 36 | watch: { |
@@ -42,12 +59,13 @@ const general = { | |||
42 | return null | 59 | return null |
43 | }, | 60 | }, |
44 | }, | 61 | }, |
62 | pathResolve(), | ||
45 | node(), | 63 | node(), |
46 | commonjs(), | 64 | commonjs(), |
47 | production && terser({ | 65 | prod && terser({ |
48 | keep_fnames: true, | 66 | keep_fnames: true, |
49 | }), | 67 | }), |
50 | production && bundleStats(), | 68 | prod && bundleStats(), |
51 | ], | 69 | ], |
52 | } | 70 | } |
53 | 71 | ||
@@ -60,4 +78,31 @@ export default [ | |||
60 | }, | 78 | }, |
61 | ] | 79 | ] |
62 | .map(config => ({ ...general, ...config })) | 80 | .map(config => ({ ...general, ...config })) |
63 | .filter((config) => production || config.input.match(/editor/)) | 81 | .filter(config => { |
82 | if (addon) return config.input.match(/dumbymap/) | ||
83 | if (!prod) return config.input.match(/editor/) | ||
84 | }) | ||
85 | .map(config => { | ||
86 | if (!addon) return config | ||
87 | |||
88 | config.output.forEach(o => o.dir = './addon') | ||
89 | config.plugins.push({ | ||
90 | name: 'remove-exports', | ||
91 | transform (code, id) { | ||
92 | if (id.includes(config.input)) { | ||
93 | // remove export keyword for addon | ||
94 | const transformedCode = code.replace(/\n(\s*)export\s*/g, '$1') | ||
95 | return { | ||
96 | code: [ | ||
97 | transformedCode, | ||
98 | 'window.generateMaps = generateMaps', | ||
99 | 'window.mapclay = mapclay', | ||
100 | ].join('\n'), | ||
101 | } | ||
102 | } | ||
103 | return null | ||
104 | }, | ||
105 | }) | ||
106 | |||
107 | return config | ||
108 | }) | ||
diff --git a/src/dumbymap.mjs b/src/dumbymap.mjs index 8e26ee6..ee61838 100644 --- a/src/dumbymap.mjs +++ b/src/dumbymap.mjs | |||
@@ -3,7 +3,7 @@ import MarkdownItAnchor from 'markdown-it-anchor' | |||
3 | import MarkdownItFootnote from 'markdown-it-footnote' | 3 | import MarkdownItFootnote from 'markdown-it-footnote' |
4 | import MarkdownItFrontMatter from 'markdown-it-front-matter' | 4 | import MarkdownItFrontMatter from 'markdown-it-front-matter' |
5 | import MarkdownItInjectLinenumbers from 'markdown-it-inject-linenumbers' | 5 | import MarkdownItInjectLinenumbers from 'markdown-it-inject-linenumbers' |
6 | import { renderWith, defaultAliases, parseConfigsFromYaml } from 'mapclay' | 6 | import * as mapclay from 'mapclay' |
7 | import { replaceTextNodes, onRemove, animateRectTransition, throttle, shiftByWindow } from './utils' | 7 | import { replaceTextNodes, onRemove, animateRectTransition, throttle, shiftByWindow } from './utils' |
8 | import { Layout, SideBySide, Overlay } from './Layout' | 8 | import { Layout, SideBySide, Overlay } from './Layout' |
9 | import * as utils from './dumbyUtils' | 9 | import * as utils from './dumbyUtils' |
@@ -86,6 +86,68 @@ export const markdown2HTML = (container, mdContent) => { | |||
86 | } | 86 | } |
87 | 87 | ||
88 | /** | 88 | /** |
89 | * defaultBlocks. | ||
90 | * @description Default way to get blocks from Semantic HTML | ||
91 | * @param {HTMLElement} root | ||
92 | */ | ||
93 | const defaultBlocks = root => { | ||
94 | const articles = root.querySelectorAll('article') | ||
95 | if (articles.length > 2) return articles | ||
96 | |||
97 | const others = Array.from( | ||
98 | root.querySelectorAll(':has(>p, >blockquote, >pre, >ul, >ol, >table, >details)'), | ||
99 | ) | ||
100 | .map(e => { | ||
101 | e.classList.add('dumby-block') | ||
102 | return e | ||
103 | }) | ||
104 | .filter(e => { | ||
105 | const isContained = e.parentElement.closest('.dumby-block') | ||
106 | if (isContained) e.classList.remove('dumby-block') | ||
107 | return !isContained | ||
108 | }) | ||
109 | |||
110 | return others | ||
111 | } | ||
112 | |||
113 | /** | ||
114 | * updateAttributeByStep. | ||
115 | * @description Update data attribute by steps of map render | ||
116 | * @param {Object} - renderer which is running steps | ||
117 | */ | ||
118 | const updateAttributeByStep = ({ results, target, steps }) => { | ||
119 | let passNum = results.filter( | ||
120 | r => r.type === 'render' && r.state.match(/success|skip/), | ||
121 | ).length | ||
122 | const total = steps.length | ||
123 | passNum += `/${total}` | ||
124 | |||
125 | const final = results.filter(r => r.type === 'render').length === total | ||
126 | |||
127 | // FIXME HACK use MutationObserver for animation | ||
128 | if (!target.animations) target.animations = Promise.resolve() | ||
129 | target.animations = target.animations.then(async () => { | ||
130 | await new Promise(resolve => setTimeout(resolve, 100)) | ||
131 | if (final) passNum += '\x20' | ||
132 | target.dataset.report = passNum | ||
133 | |||
134 | if (final) setTimeout(() => delete target.dataset.report, 100) | ||
135 | }) | ||
136 | } | ||
137 | |||
138 | /** Get default render method by converter */ | ||
139 | const defaultRender = mapclay.renderWith(config => ({ | ||
140 | use: config.use ?? 'Leaflet', | ||
141 | width: '100%', | ||
142 | ...config, | ||
143 | aliases: { | ||
144 | ...mapclay.defaultAliases, | ||
145 | ...(config.aliases ?? {}), | ||
146 | }, | ||
147 | stepCallback: updateAttributeByStep, | ||
148 | })) | ||
149 | |||
150 | /** | ||
89 | * Generates maps based on the provided configuration | 151 | * Generates maps based on the provided configuration |
90 | * | 152 | * |
91 | * @param {HTMLElement} container - The container element for the maps | 153 | * @param {HTMLElement} container - The container element for the maps |
@@ -96,16 +158,19 @@ export const markdown2HTML = (container, mdContent) => { | |||
96 | */ | 158 | */ |
97 | export const generateMaps = (container, { | 159 | export const generateMaps = (container, { |
98 | crs = 'EPSG:4326', | 160 | crs = 'EPSG:4326', |
161 | initialLayout, | ||
99 | layouts = [], | 162 | layouts = [], |
100 | delay, | 163 | delay, |
101 | renderCallback, | 164 | renderCallback, |
102 | addBlocks = htmlHolder => Array.from(htmlHolder.querySelectorAll('article')), | 165 | addBlocks = defaultBlocks, |
166 | render = defaultRender, | ||
103 | } = {}) => { | 167 | } = {}) => { |
104 | /** Prepare Contaner/HTML-Holder/Showcase */ | 168 | /** Prepare Contaner */ |
105 | container.classList.add('Dumby') | 169 | container.classList.add('Dumby') |
106 | delete container.dataset.layout | 170 | delete container.dataset.layout |
107 | container.dataset.layout = defaultLayouts[0].name | 171 | container.dataset.layout = initialLayout ?? defaultLayouts[0].name |
108 | 172 | ||
173 | /** Prepare Semantic HTML part and blocks of contents inside */ | ||
109 | const htmlHolder = container.querySelector('.SemanticHtml, :has(article, section)') ?? container.firstElementChild | 174 | const htmlHolder = container.querySelector('.SemanticHtml, :has(article, section)') ?? container.firstElementChild |
110 | htmlHolder.classList.add('.SemanticHtml') | 175 | htmlHolder.classList.add('.SemanticHtml') |
111 | const blocks = addBlocks(htmlHolder) | 176 | const blocks = addBlocks(htmlHolder) |
@@ -114,12 +179,13 @@ export const generateMaps = (container, { | |||
114 | b.dataset.total = blocks.length | 179 | b.dataset.total = blocks.length |
115 | }) | 180 | }) |
116 | 181 | ||
182 | /** Prepare Showcase */ | ||
117 | const showcase = document.createElement('div') | 183 | const showcase = document.createElement('div') |
118 | container.appendChild(showcase) | 184 | container.appendChild(showcase) |
119 | showcase.classList.add('Showcase') | 185 | showcase.classList.add('Showcase') |
120 | const renderPromises = [] | ||
121 | 186 | ||
122 | /** Prepare Modal */ | 187 | /** Prepare Other Variables */ |
188 | const renderPromises = [] | ||
123 | const modalContent = document.createElement('div') | 189 | const modalContent = document.createElement('div') |
124 | container.appendChild(modalContent) | 190 | container.appendChild(modalContent) |
125 | const modal = new PlainModal(modalContent) | 191 | const modal = new PlainModal(modalContent) |
@@ -392,63 +458,25 @@ export const generateMaps = (container, { | |||
392 | const elementsWithMapConfig = Array.from( | 458 | const elementsWithMapConfig = Array.from( |
393 | container.querySelectorAll(mapBlockSelector) ?? [], | 459 | container.querySelectorAll(mapBlockSelector) ?? [], |
394 | ) | 460 | ) |
395 | /** | 461 | if (elementsWithMapConfig.length === 0) { |
396 | * updateAttributeByStep. | 462 | const map = document.createElement('pre') |
397 | * | 463 | map.textContent = '#Created by DumbyMap' |
398 | * @param {Object} - renderer which is running steps | 464 | htmlHolder.insertBefore(map, htmlHolder.firstElementChild) |
399 | */ | 465 | elementsWithMapConfig.push(map) |
400 | const updateAttributeByStep = ({ results, target, steps }) => { | ||
401 | let passNum = results.filter( | ||
402 | r => r.type === 'render' && r.state.match(/success|skip/), | ||
403 | ).length | ||
404 | const total = steps.length | ||
405 | passNum += `/${total}` | ||
406 | |||
407 | const final = results.filter(r => r.type === 'render').length === total | ||
408 | |||
409 | // FIXME HACK use MutationObserver for animation | ||
410 | if (!target.animations) target.animations = Promise.resolve() | ||
411 | target.animations = target.animations.then(async () => { | ||
412 | await new Promise(resolve => setTimeout(resolve, 100)) | ||
413 | if (final) passNum += '\x20' | ||
414 | target.dataset.report = passNum | ||
415 | |||
416 | if (final) setTimeout(() => delete target.dataset.report, 100) | ||
417 | }) | ||
418 | } | 466 | } |
419 | /** | ||
420 | * config converter for mapclay.renderWith() | ||
421 | * | ||
422 | * @param {Object} config | ||
423 | * @return {Object} - converted config | ||
424 | */ | ||
425 | const configConverter = config => ({ | ||
426 | use: config.use ?? 'Leaflet', | ||
427 | width: '100%', | ||
428 | ...config, | ||
429 | aliases: { | ||
430 | ...defaultAliases, | ||
431 | ...(config.aliases ?? {}), | ||
432 | }, | ||
433 | stepCallback: updateAttributeByStep, | ||
434 | }) | ||
435 | |||
436 | /** Get render method by converter */ | ||
437 | const render = renderWith(configConverter) | ||
438 | 467 | ||
439 | /** Render each taget element for maps */ | 468 | /** Render each taget element for maps */ |
440 | let order = 0 | 469 | let order = 0 |
441 | elementsWithMapConfig.forEach(target => { | 470 | elementsWithMapConfig.forEach(target => { |
442 | // Get text in code block starts with markdown text '```map' | 471 | // Get text in code block starts with markdown text '```map' |
443 | const configText = target | 472 | const configText = target |
444 | .querySelector('.language-map') | ||
445 | .textContent // BE CAREFUL!!! 0xa0 char is "non-breaking spaces" in HTML text content | 473 | .textContent // BE CAREFUL!!! 0xa0 char is "non-breaking spaces" in HTML text content |
446 | // replace it by normal space | 474 | // replace it by normal space |
447 | .replace(/\u00A0/g, '\u0020') | 475 | .replace(/\u00A0/g, '\u0020') |
448 | 476 | ||
449 | let configList = [] | 477 | let configList = [] |
450 | try { | 478 | try { |
451 | configList = parseConfigsFromYaml(configText).map(assignMapId) | 479 | configList = mapclay.parseConfigsFromYaml(configText).map(assignMapId) |
452 | } catch (_) { | 480 | } catch (_) { |
453 | console.warn('Fail to parse yaml config for element', target) | 481 | console.warn('Fail to parse yaml config for element', target) |
454 | return | 482 | return |