From ce1f619b9a7a64100a0315330244a296c091bbc9 Mon Sep 17 00:00:00 2001 From: Hsieh Chin Fan Date: Mon, 28 Oct 2024 19:32:23 +0800 Subject: feat: add menu-folder for editing map options * add dumbymap.aliases for option aliases * don't remove sub-menu when child sub-menu exists * don't add render delay for config modification by menu-item * TODO sub-menu placement --- src/MenuItem.mjs | 60 ++++++++++++++++++++++++++++++++++++++++++++++++-------- src/dumbymap.mjs | 29 +++++++++++++++++++-------- 2 files changed, 73 insertions(+), 16 deletions(-) (limited to 'src') diff --git a/src/MenuItem.mjs b/src/MenuItem.mjs index 7e50991..e560b39 100644 --- a/src/MenuItem.mjs +++ b/src/MenuItem.mjs @@ -3,6 +3,7 @@ import { addMarkerByPoint } from './dumbyUtils.mjs' /* eslint-disable-next-line no-unused-vars */ import { GeoLink, getMarkersFromMaps, removeLeaderLines } from './Link.mjs' import * as markers from './marker.mjs' +import { parseConfigsFromYaml } from 'mapclay' /** * @typedef {Object} RefLink @@ -77,7 +78,10 @@ export class Folder extends window.HTMLDivElement { const offset = this.items.length > 1 ? '-20px' : '0px' submenu.style.cssText = `${style ?? ''}position: absolute; left: 105%; top: ${offset};` this.items.forEach(item => submenu.appendChild(item)) - submenu.onmouseleave = () => submenu.remove() + submenu.onmouseleave = () => { + if (submenu.querySelectorAll('.sub-menu').length > 0) return + submenu.remove() + } // hover effect this.parentElement @@ -98,7 +102,7 @@ export const simplePlaceholder = (text) => new Item({ }) /** - * Creates a menu item for picking a map + * Pick up a map * * @param {Object} options - The options object * @param {Object} options.utils - Utility functions @@ -532,20 +536,21 @@ export const addMarker = ({ /** * editByRawText. * - * @param {HTMLElement} ele + * @param {HTMLElement} map */ -export const editMapByRawText = (ele) => new Item({ +export const editMapByRawText = (map) => new Item({ text: 'Edit by Raw Text', onclick: () => { - const maps = Array.from(ele.querySelectorAll('.mapclay')) + const container = map.closest('.map-container') + const maps = Array.from(container.querySelectorAll('.mapclay')) if (!maps) return false - const rect = ele.getBoundingClientRect() + const rect = map.getBoundingClientRect() const textArea = document.createElement('textarea') textArea.className = 'edit-map' textArea.style.cssText = `width: ${rect.width}px; height: ${rect.height}px;` textArea.value = maps.map(map => map.dataset.mapclay ?? '') - .join('---') + .join('\n---\n') .replaceAll(',', '\n') .replaceAll(/["{}]/g, '') .replaceAll(/:(\w)/g, ': $1') @@ -554,10 +559,49 @@ export const editMapByRawText = (ele) => new Item({ const code = document.createElement('code') code.className = 'map' code.textContent = textArea.value + container.dataset.render = 'no-delay' textArea.replaceWith(code) }) - ele.replaceChildren(textArea) + container.replaceChildren(textArea) return true }, }) + +export const editMap = (map, dumbymap) => { + const options = Object.entries(dumbymap.aliases) + .map(([option, aliases]) => + new Folder({ + text: option, + items: Object.entries(aliases) + .map(([alias, value]) => { + const aliasValue = value.value ?? value + return new Item({ + innerHTML: `
${alias}
${aliasValue}
`, + style: 'display: flex; justify-content: space-between; max-width: 20rem;', + onclick: () => { + const container = map.closest('.map-container') + const configText = Array.from(container.querySelectorAll('.mapclay')) + .map(map => map.dataset.mapclay ?? '') + .join('\n---\n') + const configList = parseConfigsFromYaml(configText) + configList.find(config => config.id === map.id)[option] = aliasValue + const code = document.createElement('code') + code.className = 'map' + code.textContent = configList.map(JSON.stringify).join('\n---\n') + container.dataset.render = 'no-delay' + container.replaceChildren(code) + }, + }) + }), + }), + ) + return new Folder({ + text: 'Edit Map', + style: 'overflow: visible;', + items: [ + editMapByRawText(map), + ...options, + ], + }) +} diff --git a/src/dumbymap.mjs b/src/dumbymap.mjs index f538d0d..1509c04 100644 --- a/src/dumbymap.mjs +++ b/src/dumbymap.mjs @@ -135,6 +135,7 @@ const defaultRender = mapclay.renderWith(config => ({ * @param {number} [options.delay=1000] mapDelay - Delay before rendering maps (in milliseconds) * @param {Function} options.render - Render function for maps * @param {Function} options.renderCallback - Callback function to be called after map rendering + * @param {String | null} options.defaultApply */ export const generateMaps = (container, { contentSelector, @@ -144,6 +145,7 @@ export const generateMaps = (container, { mapDelay = 1000, render = defaultRender, renderCallback = () => null, + defaultApply = 'https://outdoorsafetylab.github.io/dumbymap/assets/default.yml', } = {}) => { /** Prepare: Contaner */ if (container.classList.contains('Dumby')) return @@ -179,6 +181,7 @@ export const generateMaps = (container, { get blocks () { return Array.from(container.querySelectorAll('.dumby-block')) }, modal, modalContent, + aliases: {}, utils: { ...utils, renderedMaps: () => @@ -478,6 +481,7 @@ export const generateMaps = (container, { */ function renderMap (target) { if (!target.isConnected) return + target.classList.add('map-container') // Get text in code block starts with markdown text '```map' const configText = target @@ -510,7 +514,7 @@ export const generateMaps = (container, { .forEach(e => e.remove()) } - if (!target.renderMap) { + if (!target.renderMap || target.dataset.render === 'no-delay') { target.renderMap = debounce( (configList) => { // Render maps @@ -522,7 +526,7 @@ export const generateMaps = (container, { afterMapRendered(e.renderer) } }) - }, mapDelay, + }, target.dataset.render === 'no-delay' ? 0 : mapDelay, ) } target.renderMap(configList) @@ -592,12 +596,7 @@ export const generateMaps = (container, { const rect = map.getBoundingClientRect() const [x, y] = [e.x - rect.left, e.y - rect.top] menu.appendChild(menuItem.simplePlaceholder(`MAP ID: ${map.id}`)) - menu.appendChild(new menuItem.Folder({ - text: 'Edit Map', - items: [ - menuItem.editMapByRawText(map.parentElement), - ], - })) + menu.appendChild(menuItem.editMap(map, dumbymap)) menu.appendChild(menuItem.renderResults(dumbymap, map)) if (map.dataset.render === 'fulfilled') { @@ -694,6 +693,20 @@ export const generateMaps = (container, { } } + /** Get default applied config */ + if (defaultApply) { + fetch(defaultApply) + .then(res => res.text()) + .then(rawText => { + const config = mapclay.parseConfigsFromYaml(rawText)?.at(0) + Object.entries(config.aliases) + .forEach(([option, aliases]) => { + dumbymap.aliases[option] = aliases + }) + }) + .catch(err => console.warn(`Fail to get aliases from ${defaultApply}`, err)) + } + /** Return Object for utils */ return Object.seal(dumbymap) } -- cgit v1.2.3-70-g09d2