From b3cabd0868db0b90b98e6ee6bb80943e0f4ede4d Mon Sep 17 00:00:00 2001 From: Hsieh Chin Fan Date: Fri, 20 Sep 2024 15:03:09 +0800 Subject: feat: draggable elements only appears in overlay layout --- src/css/dumbymap.css | 155 +++++++++++++++++++++++++-------------------------- src/dumbymap.mjs | 80 ++++++++++++++------------ 2 files changed, 120 insertions(+), 115 deletions(-) (limited to 'src') diff --git a/src/css/dumbymap.css b/src/css/dumbymap.css index a3f1fb9..40397c1 100644 --- a/src/css/dumbymap.css +++ b/src/css/dumbymap.css @@ -91,7 +91,7 @@ } [data-focus="true"] { - z-index: 9999; + z-index: 1; } } @@ -101,6 +101,12 @@ overflow-y: scroll; width: 100%; + .dumby-block { + border-left: #f0f0f0 0.5em solid; + padding-left: 0.5em; + margin-left: -0.5em; + } + pre { width: 100%; @@ -130,26 +136,6 @@ } } -.draggable-block { - pointer-events: auto; - background-color: white; - margin-bottom: 3rem; - - .draggable { - display: none; - } -} - -/* Left border to indicate there is a block */ -.DumbyMap[data-layout]:not([data-layout="overlay"]) { - .draggable-block{ - width: calc(100% + 0.5em); - border-left: #f0f0f0 0.5em solid; - padding-left: 0.5em; - margin-left: -0.5em; - } -} - .DumbyMap[data-layout]:not([data-layout="none"]) { margin: 0 auto; height: 100vh; @@ -174,88 +160,97 @@ flex: 50%; display: block; height: 100vh; + z-index: -1; } } .DumbyMap[data-layout="overlay"] { - .Showcase, - .SemanticHtml { - position: fixed; + .SemanticHtml, + .Showcase { + display: block; + position: absolute; + left: 0; + top: 0; + padding: 0; + margin: 0; height: 100%; width: 100%; } .Showcase { - display: block; + z-index: 0; } .SemanticHtml { - font-size: 12px; pointer-events: none; z-index: 1; + } +} - > .draggable-block { - box-sizing: content-box; - position: absolute; - width: fit-content; - max-height: 50vh; - max-width: 25vw; - overflow: scroll; - border: solid gray; - border-radius: 0.5rem; - padding-top: 0.5rem; - padding-bottom: 0; - resize: both; - - &[style*="height"], - &[style*="width"] { - max-height: unset; - max-width: unset; - } +.draggable-block { + background-color: white; + margin-bottom: 3rem; + font-size: 12px; + pointer-events: auto; + box-sizing: content-box; + position: absolute; + width: fit-content; + max-height: 50vh; + max-width: 25vw; + overflow: scroll; + border: solid gray; + border-radius: 0.5rem; + padding-top: 0.5rem; + padding-bottom: 0; + resize: both; + + &[style*="height"], + &[style*="width"] { + max-height: unset; + max-width: unset; + } - .map-container { - min-width: 200px; - } + .dumby-block { + border: none; + padding: 0; + } - > :not(.draggable) { - margin-inline: 0.5rem; - } + .map-container { + min-width: 200px; + } - .draggable { - display: block; - top: 0; - left: 0; - position: sticky; - background: linear-gradient(0deg, rgb(255 255 255 / 0%), rgb(255 255 255 / 100%) 60%); - margin-bottom: -18px; - transform: translate(0, -16px); - width: 100%; - padding-inline: 0; - padding-bottom: 2em; - text-align: center; - z-index: 9999; - transition: all 0.3s ease-in-out; - - &:hover { - background: #e1e1e1; - padding-block: 1em 0.5em; - - & ~ * { - opacity: 0.7; - color: gray; - } - } - } - } + > :not(.draggable) { + margin-inline: 0.5rem; + } - > :not(.draggable-block) { - display: none; + .draggable { + display: block; + top: 0; + left: 0; + position: sticky; + background: linear-gradient(0deg, rgb(255 255 255 / 0%), rgb(255 255 255 / 100%) 60%); + margin-bottom: -18px; + transform: translate(0, -16px); + width: 100%; + padding-inline: 0; + padding-bottom: 2em; + text-align: center; + z-index: 9999; + transition: all 0.3s ease-in-out; + + &:hover { + background: #e1e1e1; + padding-block: 1em 0.5em; + + & ~ * { + opacity: 0.7; + color: gray; + } } - } - } + .bold-options { font-weight: bold; } diff --git a/src/dumbymap.mjs b/src/dumbymap.mjs index 6e4c00e..bdb7ee8 100644 --- a/src/dumbymap.mjs +++ b/src/dumbymap.mjs @@ -86,28 +86,28 @@ export const markdown2HTML = (container, mdContent) => { .use(MarkdownItFrontMatter) .use(MarkdownItTocDoneRight) - // FIXME A better way to generate draggable code block - md.renderer.rules.draggable_block_open = () => '
' - md.renderer.rules.draggable_block_close = () => '
' + // FIXME A better way to generate blocks + md.renderer.rules.dumby_block_open = () => '
' + md.renderer.rules.dumby_block_close = () => '
' - md.core.ruler.before('block', 'draggable_block', (state) => { - state.tokens.push(new state.Token('draggable_block_open', '', 1)) + md.core.ruler.before('block', 'dumby_block', (state) => { + state.tokens.push(new state.Token('dumby_block_open', '', 1)) }) // Add close tag for block with more than 2 empty lines - md.block.ruler.before('table', 'draggable_block', (state, startLine) => { + md.block.ruler.before('table', 'dumby_block', (state, startLine) => { if ( state.src[state.bMarks[startLine - 1]] === '\n' && state.src[state.bMarks[startLine - 2]] === '\n' && state.tokens.at(-1).type !== 'list_item_open' // Quick hack for not adding tag after "::marker" for
  • ) { - state.push('draggable_block_close', '', -1); - state.push('draggable_block_open', '', 1); + state.push('dumby_block_close', '', -1); + state.push('dumby_block_open', '', 1); } }) - md.core.ruler.after('block', 'draggable_block', (state) => { - state.tokens.push(new state.Token('draggable_block_close', '', -1)) + md.core.ruler.after('block', 'dumby_block', (state) => { + state.tokens.push(new state.Token('dumby_block_close', '', -1)) }) const contentWithToc = '${toc}\n\n\n' + mdContent @@ -115,7 +115,7 @@ export const markdown2HTML = (container, mdContent) => { // TODO Do this in markdown-it htmlHolder.querySelectorAll('* > div:not(:has(nav))') - .forEach(b => b.classList.add('draggable-block')) + .forEach(b => b.classList.add('dumby-block')) return container //}}} @@ -200,33 +200,48 @@ export const generateMaps = async (container, callback) => { childRect.bottom < parentRect.bottom - offset } //}}} - // Draggable Blocks{{{ + // Draggable Blocks {{{ // Add draggable part for blocks - htmlHolder.blocks = Array.from(htmlHolder.querySelectorAll('.draggable-block')) - htmlHolder.blocks.forEach(block => { + + const dumbyBlocks = Array.from(htmlHolder.querySelectorAll('.dumby-block')) + const intoDraggableContainer = (block) => { + // Create draggable block + const draggableContainer = document.createElement('div') + draggableContainer.classList.add('draggable-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 - + // Hide block with middle click draggablePart.onmouseup = (e) => { if (e.button === 1) { - block.style.display = "none"; + draggableContainer.style.display = "none"; } } - }) + draggableContainer.appendChild(draggablePart) + draggableContainer.appendChild(block) + htmlHolder.appendChild(draggableContainer) + return draggableContainer + } + + const resumeFromDraggableContainer = (block) => { + const draggableContainer = block.closest('.draggable-block') + if (!draggableContainer) return + htmlHolder.appendChild(block) + draggableContainer.draggableInstance.remove() + draggableContainer.remove() + } // }}} // CSS observer {{{ + // Focus Map {{{ // Set focusArea const showcase = document.createElement('div') container.appendChild(showcase) showcase.classList.add('Showcase') - // Focus Map {{{ const toShowcaseWithThrottle = throttle(animateRectTransition, 300) const fromShowCaseWithThrottle = throttle(animateRectTransition, 300) @@ -331,7 +346,7 @@ export const generateMaps = async (container, callback) => { focusMap?.setAttribute('data-focus', 'true') // Check empty block with map-container in showcase - htmlHolder.blocks.forEach(b => { + dumbyBlocks.forEach(b => { const contentChildren = Array.from(b.querySelectorAll(':scope > :not(.draggable)')) ?? [] if (contentChildren.length === 1 && elementsWithMapConfig.includes(contentChildren[0]) @@ -344,32 +359,27 @@ export const generateMaps = async (container, callback) => { }) if (layout === 'overlay') { + const draggableContainers = dumbyBlocks.map(intoDraggableContainer) + + // Set initial position side by side let [x, y] = [0, 0]; - htmlHolder.blocks.forEach(block => { + draggableContainers.forEach((c) => { + // Add draggable instance - block.draggableInstance = new PlainDraggable(block, { - handle: block.draggablePart, + c.draggableInstance = new PlainDraggable(c, { + handle: c.querySelector('.draggable') ?? c, snap: { x: { step: 20 }, y: { step: 20 } }, left: x, top: y, }) - - // Set initial position side by side - x += parseInt(window.getComputedStyle(block).width) + 50 + x += parseInt(window.getComputedStyle(c).width) + 30 if (x > window.innerWidth) { y += 200 x = x % window.innerWidth } }) } else { - htmlHolder.blocks.forEach(block => { - block.removeAttribute('style') - try { - block.draggableInstance.remove() - } catch (_) { - null - } - }) + dumbyBlocks.forEach(resumeFromDraggableContainer) } }); layoutObserver.observe(container, { -- cgit v1.2.3-70-g09d2