diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/css/dumbymap.css | 155 | ||||
-rw-r--r-- | src/dumbymap.mjs | 80 |
2 files changed, 120 insertions, 115 deletions
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 @@ | |||
91 | } | 91 | } |
92 | 92 | ||
93 | [data-focus="true"] { | 93 | [data-focus="true"] { |
94 | z-index: 9999; | 94 | z-index: 1; |
95 | } | 95 | } |
96 | } | 96 | } |
97 | 97 | ||
@@ -101,6 +101,12 @@ | |||
101 | overflow-y: scroll; | 101 | overflow-y: scroll; |
102 | width: 100%; | 102 | width: 100%; |
103 | 103 | ||
104 | .dumby-block { | ||
105 | border-left: #f0f0f0 0.5em solid; | ||
106 | padding-left: 0.5em; | ||
107 | margin-left: -0.5em; | ||
108 | } | ||
109 | |||
104 | pre { | 110 | pre { |
105 | width: 100%; | 111 | width: 100%; |
106 | 112 | ||
@@ -130,26 +136,6 @@ | |||
130 | } | 136 | } |
131 | } | 137 | } |
132 | 138 | ||
133 | .draggable-block { | ||
134 | pointer-events: auto; | ||
135 | background-color: white; | ||
136 | margin-bottom: 3rem; | ||
137 | |||
138 | .draggable { | ||
139 | display: none; | ||
140 | } | ||
141 | } | ||
142 | |||
143 | /* Left border to indicate there is a block */ | ||
144 | .DumbyMap[data-layout]:not([data-layout="overlay"]) { | ||
145 | .draggable-block{ | ||
146 | width: calc(100% + 0.5em); | ||
147 | border-left: #f0f0f0 0.5em solid; | ||
148 | padding-left: 0.5em; | ||
149 | margin-left: -0.5em; | ||
150 | } | ||
151 | } | ||
152 | |||
153 | .DumbyMap[data-layout]:not([data-layout="none"]) { | 139 | .DumbyMap[data-layout]:not([data-layout="none"]) { |
154 | margin: 0 auto; | 140 | margin: 0 auto; |
155 | height: 100vh; | 141 | height: 100vh; |
@@ -174,88 +160,97 @@ | |||
174 | flex: 50%; | 160 | flex: 50%; |
175 | display: block; | 161 | display: block; |
176 | height: 100vh; | 162 | height: 100vh; |
163 | z-index: -1; | ||
177 | } | 164 | } |
178 | } | 165 | } |
179 | 166 | ||
180 | .DumbyMap[data-layout="overlay"] { | 167 | .DumbyMap[data-layout="overlay"] { |
181 | .Showcase, | 168 | .SemanticHtml, |
182 | .SemanticHtml { | 169 | .Showcase { |
183 | position: fixed; | 170 | display: block; |
171 | position: absolute; | ||
172 | left: 0; | ||
173 | top: 0; | ||
174 | padding: 0; | ||
175 | margin: 0; | ||
184 | height: 100%; | 176 | height: 100%; |
185 | width: 100%; | 177 | width: 100%; |
186 | } | 178 | } |
187 | 179 | ||
188 | .Showcase { | 180 | .Showcase { |
189 | display: block; | 181 | z-index: 0; |
190 | } | 182 | } |
191 | 183 | ||
192 | .SemanticHtml { | 184 | .SemanticHtml { |
193 | font-size: 12px; | ||
194 | pointer-events: none; | 185 | pointer-events: none; |
195 | z-index: 1; | 186 | z-index: 1; |
187 | } | ||
188 | } | ||
196 | 189 | ||
197 | > .draggable-block { | 190 | .draggable-block { |
198 | box-sizing: content-box; | 191 | background-color: white; |
199 | position: absolute; | 192 | margin-bottom: 3rem; |
200 | width: fit-content; | 193 | font-size: 12px; |
201 | max-height: 50vh; | 194 | pointer-events: auto; |
202 | max-width: 25vw; | 195 | box-sizing: content-box; |
203 | overflow: scroll; | 196 | position: absolute; |
204 | border: solid gray; | 197 | width: fit-content; |
205 | border-radius: 0.5rem; | 198 | max-height: 50vh; |
206 | padding-top: 0.5rem; | 199 | max-width: 25vw; |
207 | padding-bottom: 0; | 200 | overflow: scroll; |
208 | resize: both; | 201 | border: solid gray; |
209 | 202 | border-radius: 0.5rem; | |
210 | &[style*="height"], | 203 | padding-top: 0.5rem; |
211 | &[style*="width"] { | 204 | padding-bottom: 0; |
212 | max-height: unset; | 205 | resize: both; |
213 | max-width: unset; | 206 | |
214 | } | 207 | &[style*="height"], |
208 | &[style*="width"] { | ||
209 | max-height: unset; | ||
210 | max-width: unset; | ||
211 | } | ||
215 | 212 | ||
216 | .map-container { | 213 | .dumby-block { |
217 | min-width: 200px; | 214 | border: none; |
218 | } | 215 | padding: 0; |
216 | } | ||
219 | 217 | ||
220 | > :not(.draggable) { | 218 | .map-container { |
221 | margin-inline: 0.5rem; | 219 | min-width: 200px; |
222 | } | 220 | } |
223 | 221 | ||
224 | .draggable { | 222 | > :not(.draggable) { |
225 | display: block; | 223 | margin-inline: 0.5rem; |
226 | top: 0; | 224 | } |
227 | left: 0; | ||
228 | position: sticky; | ||
229 | background: linear-gradient(0deg, rgb(255 255 255 / 0%), rgb(255 255 255 / 100%) 60%); | ||
230 | margin-bottom: -18px; | ||
231 | transform: translate(0, -16px); | ||
232 | width: 100%; | ||
233 | padding-inline: 0; | ||
234 | padding-bottom: 2em; | ||
235 | text-align: center; | ||
236 | z-index: 9999; | ||
237 | transition: all 0.3s ease-in-out; | ||
238 | |||
239 | &:hover { | ||
240 | background: #e1e1e1; | ||
241 | padding-block: 1em 0.5em; | ||
242 | |||
243 | & ~ * { | ||
244 | opacity: 0.7; | ||
245 | color: gray; | ||
246 | } | ||
247 | } | ||
248 | } | ||
249 | } | ||
250 | 225 | ||
251 | > :not(.draggable-block) { | 226 | .draggable { |
252 | display: none; | 227 | display: block; |
228 | top: 0; | ||
229 | left: 0; | ||
230 | position: sticky; | ||
231 | background: linear-gradient(0deg, rgb(255 255 255 / 0%), rgb(255 255 255 / 100%) 60%); | ||
232 | margin-bottom: -18px; | ||
233 | transform: translate(0, -16px); | ||
234 | width: 100%; | ||
235 | padding-inline: 0; | ||
236 | padding-bottom: 2em; | ||
237 | text-align: center; | ||
238 | z-index: 9999; | ||
239 | transition: all 0.3s ease-in-out; | ||
240 | |||
241 | &:hover { | ||
242 | background: #e1e1e1; | ||
243 | padding-block: 1em 0.5em; | ||
244 | |||
245 | & ~ * { | ||
246 | opacity: 0.7; | ||
247 | color: gray; | ||
248 | } | ||
253 | } | 249 | } |
254 | |||
255 | } | 250 | } |
256 | |||
257 | } | 251 | } |
258 | 252 | ||
253 | |||
259 | .bold-options { | 254 | .bold-options { |
260 | font-weight: bold; | 255 | font-weight: bold; |
261 | } | 256 | } |
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) => { | |||
86 | .use(MarkdownItFrontMatter) | 86 | .use(MarkdownItFrontMatter) |
87 | .use(MarkdownItTocDoneRight) | 87 | .use(MarkdownItTocDoneRight) |
88 | 88 | ||
89 | // FIXME A better way to generate draggable code block | 89 | // FIXME A better way to generate blocks |
90 | md.renderer.rules.draggable_block_open = () => '<div>' | 90 | md.renderer.rules.dumby_block_open = () => '<div>' |
91 | md.renderer.rules.draggable_block_close = () => '</div>' | 91 | md.renderer.rules.dumby_block_close = () => '</div>' |
92 | 92 | ||
93 | md.core.ruler.before('block', 'draggable_block', (state) => { | 93 | md.core.ruler.before('block', 'dumby_block', (state) => { |
94 | state.tokens.push(new state.Token('draggable_block_open', '', 1)) | 94 | state.tokens.push(new state.Token('dumby_block_open', '', 1)) |
95 | }) | 95 | }) |
96 | 96 | ||
97 | // Add close tag for block with more than 2 empty lines | 97 | // Add close tag for block with more than 2 empty lines |
98 | md.block.ruler.before('table', 'draggable_block', (state, startLine) => { | 98 | md.block.ruler.before('table', 'dumby_block', (state, startLine) => { |
99 | if ( | 99 | if ( |
100 | state.src[state.bMarks[startLine - 1]] === '\n' && | 100 | state.src[state.bMarks[startLine - 1]] === '\n' && |
101 | state.src[state.bMarks[startLine - 2]] === '\n' && | 101 | state.src[state.bMarks[startLine - 2]] === '\n' && |
102 | state.tokens.at(-1).type !== 'list_item_open' // Quick hack for not adding tag after "::marker" for <li> | 102 | state.tokens.at(-1).type !== 'list_item_open' // Quick hack for not adding tag after "::marker" for <li> |
103 | ) { | 103 | ) { |
104 | state.push('draggable_block_close', '', -1); | 104 | state.push('dumby_block_close', '', -1); |
105 | state.push('draggable_block_open', '', 1); | 105 | state.push('dumby_block_open', '', 1); |
106 | } | 106 | } |
107 | }) | 107 | }) |
108 | 108 | ||
109 | md.core.ruler.after('block', 'draggable_block', (state) => { | 109 | md.core.ruler.after('block', 'dumby_block', (state) => { |
110 | state.tokens.push(new state.Token('draggable_block_close', '', -1)) | 110 | state.tokens.push(new state.Token('dumby_block_close', '', -1)) |
111 | }) | 111 | }) |
112 | 112 | ||
113 | const contentWithToc = '${toc}\n\n\n' + mdContent | 113 | const contentWithToc = '${toc}\n\n\n' + mdContent |
@@ -115,7 +115,7 @@ export const markdown2HTML = (container, mdContent) => { | |||
115 | 115 | ||
116 | // TODO Do this in markdown-it | 116 | // TODO Do this in markdown-it |
117 | htmlHolder.querySelectorAll('* > div:not(:has(nav))') | 117 | htmlHolder.querySelectorAll('* > div:not(:has(nav))') |
118 | .forEach(b => b.classList.add('draggable-block')) | 118 | .forEach(b => b.classList.add('dumby-block')) |
119 | 119 | ||
120 | return container | 120 | return container |
121 | //}}} | 121 | //}}} |
@@ -200,33 +200,48 @@ export const generateMaps = async (container, callback) => { | |||
200 | childRect.bottom < parentRect.bottom - offset | 200 | childRect.bottom < parentRect.bottom - offset |
201 | } | 201 | } |
202 | //}}} | 202 | //}}} |
203 | // Draggable Blocks{{{ | 203 | // Draggable Blocks {{{ |
204 | // Add draggable part for blocks | 204 | // Add draggable part for blocks |
205 | htmlHolder.blocks = Array.from(htmlHolder.querySelectorAll('.draggable-block')) | 205 | |
206 | htmlHolder.blocks.forEach(block => { | 206 | const dumbyBlocks = Array.from(htmlHolder.querySelectorAll('.dumby-block')) |
207 | const intoDraggableContainer = (block) => { | ||
208 | // Create draggable block | ||
209 | const draggableContainer = document.createElement('div') | ||
210 | draggableContainer.classList.add('draggable-block') | ||
211 | |||
207 | // Add draggable part | 212 | // Add draggable part |
208 | const draggablePart = document.createElement('div'); | 213 | const draggablePart = document.createElement('div'); |
209 | draggablePart.classList.add('draggable') | 214 | draggablePart.classList.add('draggable') |
210 | draggablePart.textContent = '☰' | 215 | draggablePart.textContent = '☰' |
211 | draggablePart.title = 'Use middle-click to remove block' | 216 | draggablePart.title = 'Use middle-click to remove block' |
212 | block.insertBefore(draggablePart, block.firstChild) | 217 | // Hide block with middle click |
213 | block.draggablePart = draggablePart | ||
214 | |||
215 | draggablePart.onmouseup = (e) => { | 218 | draggablePart.onmouseup = (e) => { |
216 | if (e.button === 1) { | 219 | if (e.button === 1) { |
217 | block.style.display = "none"; | 220 | draggableContainer.style.display = "none"; |
218 | } | 221 | } |
219 | } | 222 | } |
220 | }) | 223 | draggableContainer.appendChild(draggablePart) |
221 | 224 | ||
225 | draggableContainer.appendChild(block) | ||
226 | htmlHolder.appendChild(draggableContainer) | ||
227 | return draggableContainer | ||
228 | } | ||
229 | |||
230 | const resumeFromDraggableContainer = (block) => { | ||
231 | const draggableContainer = block.closest('.draggable-block') | ||
232 | if (!draggableContainer) return | ||
233 | htmlHolder.appendChild(block) | ||
234 | draggableContainer.draggableInstance.remove() | ||
235 | draggableContainer.remove() | ||
236 | } | ||
222 | // }}} | 237 | // }}} |
223 | // CSS observer {{{ | 238 | // CSS observer {{{ |
239 | // Focus Map {{{ | ||
224 | // Set focusArea | 240 | // Set focusArea |
225 | const showcase = document.createElement('div') | 241 | const showcase = document.createElement('div') |
226 | container.appendChild(showcase) | 242 | container.appendChild(showcase) |
227 | showcase.classList.add('Showcase') | 243 | showcase.classList.add('Showcase') |
228 | 244 | ||
229 | // Focus Map {{{ | ||
230 | const toShowcaseWithThrottle = throttle(animateRectTransition, 300) | 245 | const toShowcaseWithThrottle = throttle(animateRectTransition, 300) |
231 | const fromShowCaseWithThrottle = throttle(animateRectTransition, 300) | 246 | const fromShowCaseWithThrottle = throttle(animateRectTransition, 300) |
232 | 247 | ||
@@ -331,7 +346,7 @@ export const generateMaps = async (container, callback) => { | |||
331 | focusMap?.setAttribute('data-focus', 'true') | 346 | focusMap?.setAttribute('data-focus', 'true') |
332 | 347 | ||
333 | // Check empty block with map-container in showcase | 348 | // Check empty block with map-container in showcase |
334 | htmlHolder.blocks.forEach(b => { | 349 | dumbyBlocks.forEach(b => { |
335 | const contentChildren = Array.from(b.querySelectorAll(':scope > :not(.draggable)')) ?? [] | 350 | const contentChildren = Array.from(b.querySelectorAll(':scope > :not(.draggable)')) ?? [] |
336 | if (contentChildren.length === 1 | 351 | if (contentChildren.length === 1 |
337 | && elementsWithMapConfig.includes(contentChildren[0]) | 352 | && elementsWithMapConfig.includes(contentChildren[0]) |
@@ -344,32 +359,27 @@ export const generateMaps = async (container, callback) => { | |||
344 | }) | 359 | }) |
345 | 360 | ||
346 | if (layout === 'overlay') { | 361 | if (layout === 'overlay') { |
362 | const draggableContainers = dumbyBlocks.map(intoDraggableContainer) | ||
363 | |||
364 | // Set initial position side by side | ||
347 | let [x, y] = [0, 0]; | 365 | let [x, y] = [0, 0]; |
348 | htmlHolder.blocks.forEach(block => { | 366 | draggableContainers.forEach((c) => { |
367 | |||
349 | // Add draggable instance | 368 | // Add draggable instance |
350 | block.draggableInstance = new PlainDraggable(block, { | 369 | c.draggableInstance = new PlainDraggable(c, { |
351 | handle: block.draggablePart, | 370 | handle: c.querySelector('.draggable') ?? c, |
352 | snap: { x: { step: 20 }, y: { step: 20 } }, | 371 | snap: { x: { step: 20 }, y: { step: 20 } }, |
353 | left: x, | 372 | left: x, |
354 | top: y, | 373 | top: y, |
355 | }) | 374 | }) |
356 | 375 | x += parseInt(window.getComputedStyle(c).width) + 30 | |
357 | // Set initial position side by side | ||
358 | x += parseInt(window.getComputedStyle(block).width) + 50 | ||
359 | if (x > window.innerWidth) { | 376 | if (x > window.innerWidth) { |
360 | y += 200 | 377 | y += 200 |
361 | x = x % window.innerWidth | 378 | x = x % window.innerWidth |
362 | } | 379 | } |
363 | }) | 380 | }) |
364 | } else { | 381 | } else { |
365 | htmlHolder.blocks.forEach(block => { | 382 | dumbyBlocks.forEach(resumeFromDraggableContainer) |
366 | block.removeAttribute('style') | ||
367 | try { | ||
368 | block.draggableInstance.remove() | ||
369 | } catch (_) { | ||
370 | null | ||
371 | } | ||
372 | }) | ||
373 | } | 383 | } |
374 | }); | 384 | }); |
375 | layoutObserver.observe(container, { | 385 | layoutObserver.observe(container, { |