diff options
author | Hsieh Chin Fan <pham@topo.tw> | 2024-09-22 00:40:06 +0800 |
---|---|---|
committer | Hsieh Chin Fan <pham@topo.tw> | 2024-09-22 11:27:09 +0800 |
commit | f6b2421937b37173b3c732ca7834fc14053e895f (patch) | |
tree | 95a854443b5fe64f32e346af4c59c5f47c8134a3 /src | |
parent | b47a2f81a21a034073716c4c086d5fee0a1d9433 (diff) |
feat: add resize bar for side-by-side layout
* SVG comes from maplibre-gl-compare
* plain-draggable is awesome!
Diffstat (limited to 'src')
-rw-r--r-- | src/Layout.mjs | 43 | ||||
-rw-r--r-- | src/css/dumbymap.css | 99 | ||||
-rw-r--r-- | src/dumbymap.mjs | 9 |
3 files changed, 119 insertions, 32 deletions
diff --git a/src/Layout.mjs b/src/Layout.mjs index 861f56b..5673722 100644 --- a/src/Layout.mjs +++ b/src/Layout.mjs | |||
@@ -11,7 +11,48 @@ export class Layout { | |||
11 | valueOf = () => this.name | 11 | valueOf = () => this.name |
12 | } | 12 | } |
13 | 13 | ||
14 | export class OverlayLayout extends Layout { | 14 | export class SideBySide extends Layout { |
15 | name = "side-by-side" | ||
16 | |||
17 | enterHandler = ({ container, htmlHolder, showcase }) => { | ||
18 | const bar = document.createElement('div') | ||
19 | bar.className = 'bar' | ||
20 | bar.innerHTML = '<div class="bar-handle"></div>' | ||
21 | const handle = bar.querySelector('.bar-handle') | ||
22 | container.appendChild(bar) | ||
23 | |||
24 | const resizeByLeft = (left) => { | ||
25 | htmlHolder.style.width = (left) + "px" | ||
26 | showcase.style.width = (parseFloat(getComputedStyle(container).width) - left) + "px" | ||
27 | } | ||
28 | |||
29 | const draggable = new PlainDraggable(bar, { | ||
30 | handle: handle, | ||
31 | containment: { left: '25%', top: 0, right: '75%', height: 0 }, | ||
32 | }) | ||
33 | draggable.draggableCursor = "grab" | ||
34 | draggable.onDrag = (pos) => { | ||
35 | handle.style.transform = 'unset' | ||
36 | resizeByLeft(pos.left) | ||
37 | } | ||
38 | draggable.onDragEnd = (_) => { | ||
39 | handle.removeAttribute('style') | ||
40 | } | ||
41 | |||
42 | new ResizeObserver(() => { | ||
43 | if (draggable) resizeByLeft(draggable.left) | ||
44 | }).observe(container); | ||
45 | } | ||
46 | |||
47 | leaveHandler = ({ container, htmlHolder, showcase }) => { | ||
48 | container.removeAttribute('style') | ||
49 | htmlHolder.removeAttribute('style') | ||
50 | showcase.removeAttribute('style') | ||
51 | container.querySelector('.bar')?.remove() | ||
52 | } | ||
53 | } | ||
54 | |||
55 | export class Overlay extends Layout { | ||
15 | name = "overlay" | 56 | name = "overlay" |
16 | 57 | ||
17 | enterHandler = (dumbymap) => { | 58 | enterHandler = (dumbymap) => { |
diff --git a/src/css/dumbymap.css b/src/css/dumbymap.css index 68a73c0..f58cf22 100644 --- a/src/css/dumbymap.css +++ b/src/css/dumbymap.css | |||
@@ -80,7 +80,6 @@ | |||
80 | } | 80 | } |
81 | 81 | ||
82 | .DumbyMap { | 82 | .DumbyMap { |
83 | display: flex; | ||
84 | width: 100%; | 83 | width: 100%; |
85 | height: 100%; | 84 | height: 100%; |
86 | margin: 0 auto; | 85 | margin: 0 auto; |
@@ -89,32 +88,10 @@ | |||
89 | position: relative; | 88 | position: relative; |
90 | overflow-x: auto; | 89 | overflow-x: auto; |
91 | 90 | ||
92 | .Showcase { | ||
93 | display: none; | ||
94 | overflow: visible; | ||
95 | |||
96 | position: relative; | ||
97 | order: 2; | ||
98 | |||
99 | > * { | ||
100 | width: 100%; | ||
101 | height: 100%; | ||
102 | |||
103 | position: absolute; | ||
104 | top: 0; | ||
105 | left: 0; | ||
106 | } | ||
107 | |||
108 | [data-focus="true"] { | ||
109 | z-index: 1; | ||
110 | } | ||
111 | } | ||
112 | |||
113 | .SemanticHtml { | 91 | .SemanticHtml { |
114 | width: 100%; | 92 | height: 100%; |
115 | padding: 1.5rem; | 93 | padding: 1.5rem; |
116 | overflow-y: auto; | 94 | overflow-y: auto; |
117 | order: 1; | ||
118 | 95 | ||
119 | pre { | 96 | pre { |
120 | width: 100%; | 97 | width: 100%; |
@@ -149,6 +126,28 @@ | |||
149 | animation: fade-out 1s; | 126 | animation: fade-out 1s; |
150 | } | 127 | } |
151 | } | 128 | } |
129 | |||
130 | .Showcase { | ||
131 | display: none; | ||
132 | overflow: visible; | ||
133 | height: 100%; | ||
134 | |||
135 | position: relative; | ||
136 | |||
137 | > * { | ||
138 | width: 100%; | ||
139 | height: 100%; | ||
140 | |||
141 | position: absolute; | ||
142 | top: 0; | ||
143 | left: 0; | ||
144 | } | ||
145 | |||
146 | [data-focus="true"] { | ||
147 | z-index: 1; | ||
148 | } | ||
149 | } | ||
150 | |||
152 | } | 151 | } |
153 | 152 | ||
154 | .DumbyMap[data-layout="normal"] { | 153 | .DumbyMap[data-layout="normal"] { |
@@ -156,14 +155,60 @@ | |||
156 | } | 155 | } |
157 | 156 | ||
158 | .DumbyMap[data-layout="side-by-side"] { | 157 | .DumbyMap[data-layout="side-by-side"] { |
159 | display: flex; | ||
160 | |||
161 | .SemanticHtml, | 158 | .SemanticHtml, |
162 | .Showcase { | 159 | .Showcase { |
163 | flex: 50%; | 160 | width: 50%; |
164 | 161 | ||
162 | position: absolute; | ||
163 | top: 0; | ||
164 | min-width: 20%; | ||
165 | } | ||
166 | |||
167 | .SemanticHtml { | ||
168 | left: 0; | ||
169 | } | ||
170 | |||
171 | .Showcase { | ||
165 | display: block; | 172 | display: block; |
173 | |||
174 | right: 0; | ||
175 | } | ||
176 | |||
177 | .bar { | ||
178 | display: flex; | ||
179 | overflow: visible; | ||
180 | width: 1px; | ||
166 | height: 100%; | 181 | height: 100%; |
182 | |||
183 | position: absolute; | ||
184 | left: 50%; | ||
185 | z-index: 9999; | ||
186 | |||
187 | border: 2px black solid; | ||
188 | |||
189 | .bar-handle { | ||
190 | display: inline-block; | ||
191 | width: 60px; | ||
192 | height: 60px; | ||
193 | |||
194 | position: absolute; | ||
195 | top: calc(50% - 30px); | ||
196 | left: -30px; | ||
197 | |||
198 | border-radius: 50%; | ||
199 | |||
200 | background-color: #3887be; | ||
201 | background-image: url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+PHN2ZyAgIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgICB4bWxuczpjYz0iaHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbnMjIiAgIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyIgICB4bWxuczpzdmc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiAgIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgICB4bWxuczpzb2RpcG9kaT0iaHR0cDovL3NvZGlwb2RpLnNvdXJjZWZvcmdlLm5ldC9EVEQvc29kaXBvZGktMC5kdGQiICAgeG1sbnM6aW5rc2NhcGU9Imh0dHA6Ly93d3cuaW5rc2NhcGUub3JnL25hbWVzcGFjZXMvaW5rc2NhcGUiICAgd2lkdGg9IjYwIiAgIGhlaWdodD0iNjAiICAgdmVyc2lvbj0iMS4xIiAgIHZpZXdCb3g9IjAgMCA2MCA2MCIgICBpZD0ic3ZnNTQzNCIgICBpbmtzY2FwZTp2ZXJzaW9uPSIwLjkxK2RldmVsK29zeG1lbnUgcjEyOTExIiAgIHNvZGlwb2RpOmRvY25hbWU9Imwtci5zdmciPiAgPG1ldGFkYXRhICAgICBpZD0ibWV0YWRhdGE1NDQ0Ij4gICAgPHJkZjpSREY+ICAgICAgPGNjOldvcmsgICAgICAgICByZGY6YWJvdXQ9IiI+ICAgICAgICA8ZGM6Zm9ybWF0PmltYWdlL3N2Zyt4bWw8L2RjOmZvcm1hdD4gICAgICAgIDxkYzp0eXBlICAgICAgICAgICByZGY6cmVzb3VyY2U9Imh0dHA6Ly9wdXJsLm9yZy9kYy9kY21pdHlwZS9TdGlsbEltYWdlIiAvPiAgICAgICAgPGRjOnRpdGxlPjwvZGM6dGl0bGU+ICAgICAgPC9jYzpXb3JrPiAgICA8L3JkZjpSREY+ICA8L21ldGFkYXRhPiAgPGRlZnMgICAgIGlkPSJkZWZzNTQ0MiIgLz4gIDxzb2RpcG9kaTpuYW1lZHZpZXcgICAgIHBhZ2Vjb2xvcj0iI2ZmZmZmZiIgICAgIGJvcmRlcmNvbG9yPSIjNjY2NjY2IiAgICAgYm9yZGVyb3BhY2l0eT0iMSIgICAgIG9iamVjdHRvbGVyYW5jZT0iMTAiICAgICBncmlkdG9sZXJhbmNlPSIxMCIgICAgIGd1aWRldG9sZXJhbmNlPSIxMCIgICAgIGlua3NjYXBlOnBhZ2VvcGFjaXR5PSIwIiAgICAgaW5rc2NhcGU6cGFnZXNoYWRvdz0iMiIgICAgIGlua3NjYXBlOndpbmRvdy13aWR0aD0iMTI4NiIgICAgIGlua3NjYXBlOndpbmRvdy1oZWlnaHQ9Ijc1MSIgICAgIGlkPSJuYW1lZHZpZXc1NDQwIiAgICAgc2hvd2dyaWQ9InRydWUiICAgICBpbmtzY2FwZTp6b29tPSI0IiAgICAgaW5rc2NhcGU6Y3g9IjI1Ljg4OTgzMSIgICAgIGlua3NjYXBlOmN5PSIzNC4zODE4MzMiICAgICBpbmtzY2FwZTp3aW5kb3cteD0iMCIgICAgIGlua3NjYXBlOndpbmRvdy15PSIyMyIgICAgIGlua3NjYXBlOndpbmRvdy1tYXhpbWl6ZWQ9IjAiICAgICBpbmtzY2FwZTpjdXJyZW50LWxheWVyPSJzdmc1NDM0IiAgICAgaW5rc2NhcGU6b2JqZWN0LW5vZGVzPSJ0cnVlIiAgICAgaW5rc2NhcGU6c25hcC1zbW9vdGgtbm9kZXM9InRydWUiPiAgICA8aW5rc2NhcGU6Z3JpZCAgICAgICB0eXBlPSJ4eWdyaWQiICAgICAgIGlkPSJncmlkNTk4OSIgLz4gIDwvc29kaXBvZGk6bmFtZWR2aWV3PiAgPHBhdGggICAgIHN0eWxlPSJmaWxsOiNmZmZmZmY7ZmlsbC1ydWxlOmV2ZW5vZGQ7c3Ryb2tlOm5vbmU7c3Ryb2tlLXdpZHRoOjFweDtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2Utb3BhY2l0eToxIiAgICAgZD0iTSAyNSAyNCBMIDE2IDMwIEwgMjUgMzYgTCAyNSAyNCB6IE0gMzUgMjQgTCAzNSAzNiBMIDQ0IDMwIEwgMzUgMjQgeiAiICAgICBpZD0icGF0aDU5OTUiIC8+PC9zdmc+"); | ||
202 | |||
203 | transition: transform 0.3s ease-in-out; | ||
204 | transform: scale(0.5, 0.5); | ||
205 | box-shadow: inset 0 0 0 2px white; | ||
206 | |||
207 | &:hover { | ||
208 | transform: unset; | ||
209 | } | ||
210 | } | ||
211 | |||
167 | } | 212 | } |
168 | } | 213 | } |
169 | 214 | ||
diff --git a/src/dumbymap.mjs b/src/dumbymap.mjs index a785d6f..87ea694 100644 --- a/src/dumbymap.mjs +++ b/src/dumbymap.mjs | |||
@@ -6,15 +6,15 @@ import MarkdownItTocDoneRight from 'markdown-it-toc-done-right' | |||
6 | import LeaderLine from 'leader-line' | 6 | import LeaderLine from 'leader-line' |
7 | import { renderWith, defaultAliases, parseConfigsFromYaml } from 'mapclay' | 7 | import { renderWith, defaultAliases, parseConfigsFromYaml } from 'mapclay' |
8 | import { onRemove, animateRectTransition, throttle } from './utils' | 8 | import { onRemove, animateRectTransition, throttle } from './utils' |
9 | import { Layout, OverlayLayout } from './Layout' | 9 | import { Layout, SideBySide, Overlay } from './Layout' |
10 | 10 | ||
11 | const docLinkSelector = 'a[href^="#"][title^="=>"]' | 11 | const docLinkSelector = 'a[href^="#"][title^="=>"]' |
12 | const geoLinkSelector = 'a[href^="geo:"]' | 12 | const geoLinkSelector = 'a[href^="geo:"]' |
13 | 13 | ||
14 | const layouts = [ | 14 | const layouts = [ |
15 | new Layout({ name: "normal" }), | 15 | new Layout({ name: "normal" }), |
16 | new Layout({ name: "side-by-side" }), | 16 | new SideBySide({ name: "side-by-side" }), |
17 | new OverlayLayout({ name: "overlay" }), | 17 | new Overlay({ name: "overlay" }), |
18 | ] | 18 | ] |
19 | 19 | ||
20 | // FUNCTION: Get DocLinks from special anchor element {{{ | 20 | // FUNCTION: Get DocLinks from special anchor element {{{ |
@@ -144,9 +144,10 @@ export const generateMaps = async (container, callback) => { | |||
144 | showcase.classList.add('Showcase') | 144 | showcase.classList.add('Showcase') |
145 | 145 | ||
146 | const dumbymap = { | 146 | const dumbymap = { |
147 | blocks: Array.from(htmlHolder.querySelectorAll('.dumby-block')), | 147 | container: container, |
148 | htmlHolder: htmlHolder, | 148 | htmlHolder: htmlHolder, |
149 | showcase: showcase, | 149 | showcase: showcase, |
150 | blocks: Array.from(htmlHolder.querySelectorAll('.dumby-block')), | ||
150 | } | 151 | } |
151 | 152 | ||
152 | container.classList.add('DumbyMap') | 153 | container.classList.add('DumbyMap') |