From 1caf56dc0a2bbf4bb484fe4f312b5229a63aace3 Mon Sep 17 00:00:00 2001 From: Hsieh Chin Fan Date: Wed, 16 Oct 2024 16:57:47 +0800 Subject: feat: add simple script to test mapclay * add more options for generateMaps for general purposes * add rollup config for adding script into addon --- addon/index.mjs | 17 +++++++ addon/manifest.json | 10 +++- scripts/rollup.config.js | 55 ++++++++++++++++++-- src/dumbymap.mjs | 128 +++++++++++++++++++++++++++++------------------ 4 files changed, 153 insertions(+), 57 deletions(-) create mode 100644 addon/index.mjs 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 @@ +const { Leaflet } = window.mapclay.renderers +const simpleRender = window.mapclay.renderWith(config => ({ + use: 'Leaflet', + width: '100%', + height: '200px', + XYZ: 'https://tile.openstreetmap.jp/styles/osm-bright/512/{z}/{x}/{y}.png', + ...config, + aliases: { + use: { Leaflet }, + ...(config.aliases ?? {}), + }, +})) + +window.generateMaps(document.querySelector('main') ?? document.body, { + initialLayout: '', + render: simpleRender, +}) 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 @@ "content_scripts": [ { - "matches": ["*://*.mozilla.org/*"], - "js": ["index.mjs"], + "matches": [ + "*://*.mozilla.org/*", + "*://hackmd.io/*" + ], + "js": [ + "dumbymap.mjs", + "index.mjs" + ], "css": [ "css/dumbymap.css" ] 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' import { join } from 'path' import { bundleStats } from 'rollup-plugin-bundle-stats' -const production = !process.env.ROLLUP_WATCH +const prod = process.env.PRODUCTION +const addon = process.env.ADDON + +function resolve (file, origin) { + // Your way to resolve local include path +} + +function pathResolve (options) { + return { + resolveId: function (file, origin) { + // Your local include path must either starts with `./` or `../` + if (file.startsWith('./') || file.startsWith('../')) { + // Return an absolute include path + return resolve(file, origin) + } + return null // Continue to the next plugins! + }, + } +} const general = { output: [ @@ -13,7 +31,6 @@ const general = { dir: './dist', format: 'esm', entryFileNames: '[name].mjs', - sourcemap: 'true', }, ], watch: { @@ -42,12 +59,13 @@ const general = { return null }, }, + pathResolve(), node(), commonjs(), - production && terser({ + prod && terser({ keep_fnames: true, }), - production && bundleStats(), + prod && bundleStats(), ], } @@ -60,4 +78,31 @@ export default [ }, ] .map(config => ({ ...general, ...config })) - .filter((config) => production || config.input.match(/editor/)) + .filter(config => { + if (addon) return config.input.match(/dumbymap/) + if (!prod) return config.input.match(/editor/) + }) + .map(config => { + if (!addon) return config + + config.output.forEach(o => o.dir = './addon') + config.plugins.push({ + name: 'remove-exports', + transform (code, id) { + if (id.includes(config.input)) { + // remove export keyword for addon + const transformedCode = code.replace(/\n(\s*)export\s*/g, '$1') + return { + code: [ + transformedCode, + 'window.generateMaps = generateMaps', + 'window.mapclay = mapclay', + ].join('\n'), + } + } + return null + }, + }) + + return config + }) 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' import MarkdownItFootnote from 'markdown-it-footnote' import MarkdownItFrontMatter from 'markdown-it-front-matter' import MarkdownItInjectLinenumbers from 'markdown-it-inject-linenumbers' -import { renderWith, defaultAliases, parseConfigsFromYaml } from 'mapclay' +import * as mapclay from 'mapclay' import { replaceTextNodes, onRemove, animateRectTransition, throttle, shiftByWindow } from './utils' import { Layout, SideBySide, Overlay } from './Layout' import * as utils from './dumbyUtils' @@ -85,6 +85,68 @@ export const markdown2HTML = (container, mdContent) => { return container } +/** + * defaultBlocks. + * @description Default way to get blocks from Semantic HTML + * @param {HTMLElement} root + */ +const defaultBlocks = root => { + const articles = root.querySelectorAll('article') + if (articles.length > 2) return articles + + const others = Array.from( + root.querySelectorAll(':has(>p, >blockquote, >pre, >ul, >ol, >table, >details)'), + ) + .map(e => { + e.classList.add('dumby-block') + return e + }) + .filter(e => { + const isContained = e.parentElement.closest('.dumby-block') + if (isContained) e.classList.remove('dumby-block') + return !isContained + }) + + return others +} + +/** + * updateAttributeByStep. + * @description Update data attribute by steps of map render + * @param {Object} - renderer which is running steps + */ +const updateAttributeByStep = ({ results, target, steps }) => { + let passNum = results.filter( + r => r.type === 'render' && r.state.match(/success|skip/), + ).length + const total = steps.length + passNum += `/${total}` + + const final = results.filter(r => r.type === 'render').length === total + + // FIXME HACK use MutationObserver for animation + if (!target.animations) target.animations = Promise.resolve() + target.animations = target.animations.then(async () => { + await new Promise(resolve => setTimeout(resolve, 100)) + if (final) passNum += '\x20' + target.dataset.report = passNum + + if (final) setTimeout(() => delete target.dataset.report, 100) + }) +} + +/** Get default render method by converter */ +const defaultRender = mapclay.renderWith(config => ({ + use: config.use ?? 'Leaflet', + width: '100%', + ...config, + aliases: { + ...mapclay.defaultAliases, + ...(config.aliases ?? {}), + }, + stepCallback: updateAttributeByStep, +})) + /** * Generates maps based on the provided configuration * @@ -96,16 +158,19 @@ export const markdown2HTML = (container, mdContent) => { */ export const generateMaps = (container, { crs = 'EPSG:4326', + initialLayout, layouts = [], delay, renderCallback, - addBlocks = htmlHolder => Array.from(htmlHolder.querySelectorAll('article')), + addBlocks = defaultBlocks, + render = defaultRender, } = {}) => { - /** Prepare Contaner/HTML-Holder/Showcase */ + /** Prepare Contaner */ container.classList.add('Dumby') delete container.dataset.layout - container.dataset.layout = defaultLayouts[0].name + container.dataset.layout = initialLayout ?? defaultLayouts[0].name + /** Prepare Semantic HTML part and blocks of contents inside */ const htmlHolder = container.querySelector('.SemanticHtml, :has(article, section)') ?? container.firstElementChild htmlHolder.classList.add('.SemanticHtml') const blocks = addBlocks(htmlHolder) @@ -114,12 +179,13 @@ export const generateMaps = (container, { b.dataset.total = blocks.length }) + /** Prepare Showcase */ const showcase = document.createElement('div') container.appendChild(showcase) showcase.classList.add('Showcase') - const renderPromises = [] - /** Prepare Modal */ + /** Prepare Other Variables */ + const renderPromises = [] const modalContent = document.createElement('div') container.appendChild(modalContent) const modal = new PlainModal(modalContent) @@ -392,63 +458,25 @@ export const generateMaps = (container, { const elementsWithMapConfig = Array.from( container.querySelectorAll(mapBlockSelector) ?? [], ) - /** - * updateAttributeByStep. - * - * @param {Object} - renderer which is running steps - */ - const updateAttributeByStep = ({ results, target, steps }) => { - let passNum = results.filter( - r => r.type === 'render' && r.state.match(/success|skip/), - ).length - const total = steps.length - passNum += `/${total}` - - const final = results.filter(r => r.type === 'render').length === total - - // FIXME HACK use MutationObserver for animation - if (!target.animations) target.animations = Promise.resolve() - target.animations = target.animations.then(async () => { - await new Promise(resolve => setTimeout(resolve, 100)) - if (final) passNum += '\x20' - target.dataset.report = passNum - - if (final) setTimeout(() => delete target.dataset.report, 100) - }) + if (elementsWithMapConfig.length === 0) { + const map = document.createElement('pre') + map.textContent = '#Created by DumbyMap' + htmlHolder.insertBefore(map, htmlHolder.firstElementChild) + elementsWithMapConfig.push(map) } - /** - * config converter for mapclay.renderWith() - * - * @param {Object} config - * @return {Object} - converted config - */ - const configConverter = config => ({ - use: config.use ?? 'Leaflet', - width: '100%', - ...config, - aliases: { - ...defaultAliases, - ...(config.aliases ?? {}), - }, - stepCallback: updateAttributeByStep, - }) - - /** Get render method by converter */ - const render = renderWith(configConverter) /** Render each taget element for maps */ let order = 0 elementsWithMapConfig.forEach(target => { // Get text in code block starts with markdown text '```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) + configList = mapclay.parseConfigsFromYaml(configText).map(assignMapId) } catch (_) { console.warn('Fail to parse yaml config for element', target) return -- cgit v1.2.3-70-g09d2