aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/Layout.mjs
diff options
context:
space:
mode:
authorHsieh Chin Fan <pham@topo.tw>2024-09-28 17:47:52 +0800
committerHsieh Chin Fan <pham@topo.tw>2024-09-28 17:47:52 +0800
commit933eba7dc3bdc979fefadd47388b20b8360e1d6b (patch)
tree9b88908d88a025aaaa1d70306afde8e49fedab27 /src/Layout.mjs
parent5a64dfc8bb8169e72b7c1164f18c6912e9e3576e (diff)
style: prettier
Diffstat (limited to 'src/Layout.mjs')
-rw-r--r--src/Layout.mjs240
1 files changed, 120 insertions, 120 deletions
diff --git a/src/Layout.mjs b/src/Layout.mjs
index abefd4d..9cf2ab9 100644
--- a/src/Layout.mjs
+++ b/src/Layout.mjs
@@ -1,208 +1,208 @@
1import PlainDraggable from 'plain-draggable' 1import PlainDraggable from "plain-draggable";
2import { onRemove, animateRectTransition } from './utils' 2import { onRemove, animateRectTransition } from "./utils";
3 3
4export class Layout { 4export class Layout {
5 constructor(options = {}) { 5 constructor(options = {}) {
6 if (!options.name) throw Error("Layout name is not given") 6 if (!options.name) throw Error("Layout name is not given");
7 this.name = options.name 7 this.name = options.name;
8 this.enterHandler = options.enterHandler 8 this.enterHandler = options.enterHandler;
9 this.leaveHandler = options.leaveHandler 9 this.leaveHandler = options.leaveHandler;
10 } 10 }
11 valueOf = () => this.name 11 valueOf = () => this.name;
12} 12}
13 13
14export class SideBySide extends Layout { 14export class SideBySide extends Layout {
15 name = "side-by-side" 15 name = "side-by-side";
16 16
17 enterHandler = ({ container, htmlHolder, showcase }) => { 17 enterHandler = ({ container, htmlHolder, showcase }) => {
18 const bar = document.createElement('div') 18 const bar = document.createElement("div");
19 bar.className = 'bar' 19 bar.className = "bar";
20 bar.innerHTML = '<div class="bar-handle"></div>' 20 bar.innerHTML = '<div class="bar-handle"></div>';
21 const handle = bar.querySelector('.bar-handle') 21 const handle = bar.querySelector(".bar-handle");
22 container.appendChild(bar) 22 container.appendChild(bar);
23 23
24 // Resize views by value 24 // Resize views by value
25 const resizeByLeft = (left) => { 25 const resizeByLeft = left => {
26 htmlHolder.style.width = (left) + "px" 26 htmlHolder.style.width = left + "px";
27 showcase.style.width = (parseFloat(getComputedStyle(container).width) - left) + "px" 27 showcase.style.width =
28 } 28 parseFloat(getComputedStyle(container).width) - left + "px";
29 };
29 30
30 const draggable = new PlainDraggable(bar, { 31 const draggable = new PlainDraggable(bar, {
31 handle: handle, 32 handle: handle,
32 containment: { left: '25%', top: 0, right: '75%', height: 0 }, 33 containment: { left: "25%", top: 0, right: "75%", height: 0 },
33 }) 34 });
34 draggable.draggableCursor = "grab" 35 draggable.draggableCursor = "grab";
35 36
36 draggable.onDrag = (pos) => { 37 draggable.onDrag = pos => {
37 handle.style.transform = 'unset' 38 handle.style.transform = "unset";
38 resizeByLeft(pos.left) 39 resizeByLeft(pos.left);
39 } 40 };
40 draggable.onDragEnd = (_) => { 41 draggable.onDragEnd = _ => {
41 handle.removeAttribute('style') 42 handle.removeAttribute("style");
42 } 43 };
43 44
44 onRemove(bar, () => draggable.remove()) 45 onRemove(bar, () => draggable.remove());
45 } 46 };
46 47
47 leaveHandler = ({ container }) => { 48 leaveHandler = ({ container }) => {
48 container.querySelector('.bar')?.remove() 49 container.querySelector(".bar")?.remove();
49 } 50 };
50} 51}
51 52
52export class Overlay extends Layout { 53export class Overlay extends Layout {
53 name = "overlay" 54 name = "overlay";
54 55
55 saveLeftTopAsData = (element) => { 56 saveLeftTopAsData = element => {
56 const { left, top } = element.getBoundingClientRect() 57 const { left, top } = element.getBoundingClientRect();
57 element.setAttribute('data-left', left) 58 element.setAttribute("data-left", left);
58 element.setAttribute('data-top', top) 59 element.setAttribute("data-top", top);
59 } 60 };
60 61
61 addDraggable = (element) => { 62 addDraggable = element => {
62 // Make sure current element always on top 63 // Make sure current element always on top
63 const siblings = Array.from(element.parentElement?.querySelectorAll(':scope > *') ?? []) 64 const siblings = Array.from(
64 let popTimer = null 65 element.parentElement?.querySelectorAll(":scope > *") ?? [],
66 );
67 let popTimer = null;
65 element.onmouseover = () => { 68 element.onmouseover = () => {
66 popTimer = setTimeout(() => { 69 popTimer = setTimeout(() => {
67 siblings.forEach(e => e.style.removeProperty('z-index')) 70 siblings.forEach(e => e.style.removeProperty("z-index"));
68 element.style.zIndex = '9001' 71 element.style.zIndex = "9001";
69 }, 200) 72 }, 200);
70 } 73 };
71 element.onmouseout = () => { 74 element.onmouseout = () => {
72 clearTimeout(popTimer) 75 clearTimeout(popTimer);
73 } 76 };
74 77
75 // Add draggable part 78 // Add draggable part
76 const draggablePart = document.createElement('div') 79 const draggablePart = document.createElement("div");
77 element.appendChild(draggablePart) 80 element.appendChild(draggablePart);
78 draggablePart.className = 'draggable-part' 81 draggablePart.className = "draggable-part";
79 draggablePart.innerHTML = '<div class="handle">\u2630</div>' 82 draggablePart.innerHTML = '<div class="handle">\u2630</div>';
80 83
81 // Add draggable instance 84 // Add draggable instance
82 const { left, top } = element.getBoundingClientRect() 85 const { left, top } = element.getBoundingClientRect();
83 const draggable = new PlainDraggable(element, { 86 const draggable = new PlainDraggable(element, {
84 top: top, 87 top: top,
85 left: left, 88 left: left,
86 handle: draggablePart, 89 handle: draggablePart,
87 snap: { x: { step: 20 }, y: { step: 20 } }, 90 snap: { x: { step: 20 }, y: { step: 20 } },
88 }) 91 });
89 92
90 // FIXME use pure CSS to hide utils 93 // FIXME use pure CSS to hide utils
91 const utils = element.querySelector('.utils') 94 const utils = element.querySelector(".utils");
92 draggable.onDragStart = () => { 95 draggable.onDragStart = () => {
93 utils.style.display = 'none' 96 utils.style.display = "none";
94 element.classList.add('drag') 97 element.classList.add("drag");
95 } 98 };
96 99
97 draggable.onDragEnd = () => { 100 draggable.onDragEnd = () => {
98 utils.style = '' 101 utils.style = "";
99 element.classList.remove('drag') 102 element.classList.remove("drag");
100 element.style.zIndex = '9000' 103 element.style.zIndex = "9000";
101 } 104 };
102 105
103 // Reposition draggable instance when resized 106 // Reposition draggable instance when resized
104 new ResizeObserver(() => { 107 new ResizeObserver(() => {
105 try { 108 try {
106 draggable.position(); 109 draggable.position();
107 } catch (_) { 110 } catch (_) {
108 null 111 null;
109 } 112 }
110 }).observe(element); 113 }).observe(element);
111 114
112 // Callback for remove 115 // Callback for remove
113 onRemove(element, () => { 116 onRemove(element, () => {
114 draggable.remove() 117 draggable.remove();
115 }) 118 });
116 } 119 };
117 120
118 enterHandler = ({ htmlHolder, blocks }) => { 121 enterHandler = ({ htmlHolder, blocks }) => {
119 // FIXME It is weird rect from this method and this scope are different... 122 // FIXME It is weird rect from this method and this scope are different...
120 blocks.forEach(this.saveLeftTopAsData) 123 blocks.forEach(this.saveLeftTopAsData);
121 124
122 // Create draggable blocks and set each position by previous one 125 // Create draggable blocks and set each position by previous one
123 let [left, top] = [20, 20] 126 let [left, top] = [20, 20];
124 blocks.forEach(block => { 127 blocks.forEach(block => {
125 const originLeft = Number(block.getAttribute('data-left')) 128 const originLeft = Number(block.getAttribute("data-left"));
126 const originTop = Number(block.getAttribute('data-top')) 129 const originTop = Number(block.getAttribute("data-top"));
127 130
128 // Create draggable block 131 // Create draggable block
129 const wrapper = document.createElement('div') 132 const wrapper = document.createElement("div");
130 wrapper.classList.add('draggable-block') 133 wrapper.classList.add("draggable-block");
131 wrapper.innerHTML = ` 134 wrapper.innerHTML = `
132 <div class="utils"> 135 <div class="utils">
133 <div id="close">\u274C</div> 136 <div id="close">\u274C</div>
134 <div id="plus-font-size" ">\u2795</div> 137 <div id="plus-font-size" ">\u2795</div>
135 <div id="minus-font-size">\u2796</div> 138 <div id="minus-font-size">\u2796</div>
136 </div> 139 </div>
137 ` 140 `;
138 wrapper.title = 'Middle-click to hide block' 141 wrapper.title = "Middle-click to hide block";
139 wrapper.onmouseup = (e) => { 142 wrapper.onmouseup = e => {
140 // Hide block with middle click 143 // Hide block with middle click
141 if (e.button === 1) { 144 if (e.button === 1) {
142 wrapper.classList.add('hide') 145 wrapper.classList.add("hide");
143 } 146 }
144 } 147 };
145 148
146 // Set DOMRect for wrapper 149 // Set DOMRect for wrapper
147 wrapper.appendChild(block) 150 wrapper.appendChild(block);
148 wrapper.style.left = left + "px" 151 wrapper.style.left = left + "px";
149 wrapper.style.top = top + "px" 152 wrapper.style.top = top + "px";
150 htmlHolder.appendChild(wrapper) 153 htmlHolder.appendChild(wrapper);
151 const { width } = wrapper.getBoundingClientRect() 154 const { width } = wrapper.getBoundingClientRect();
152 left += width + 30 155 left += width + 30;
153 if (left > window.innerWidth) { 156 if (left > window.innerWidth) {
154 top += 200 157 top += 200;
155 left = left % window.innerWidth 158 left = left % window.innerWidth;
156 } 159 }
157 160
158 // Animation for DOMRect 161 // Animation for DOMRect
159 animateRectTransition( 162 animateRectTransition(
160 wrapper, 163 wrapper,
161 { left: originLeft, top: originTop }, 164 { left: originLeft, top: originTop },
162 { resume: true, duration: 300 } 165 { resume: true, duration: 300 },
163 ) 166 ).finished.finally(() => this.addDraggable(wrapper));
164 .finished
165 .finally(() => this.addDraggable(wrapper))
166 167
167 // Trivial case: 168 // Trivial case:
168 // This hack make sure utils remains at the same place even when wrapper resized 169 // This hack make sure utils remains at the same place even when wrapper resized
169 // Prevent DOMRect changes when user clicking plus/minus button many times 170 // Prevent DOMRect changes when user clicking plus/minus button many times
170 const utils = wrapper.querySelector('.utils') 171 const utils = wrapper.querySelector(".utils");
171 utils.onmouseover = () => { 172 utils.onmouseover = () => {
172 const { left, top } = utils.getBoundingClientRect() 173 const { left, top } = utils.getBoundingClientRect();
173 utils.style.cssText = `visibility: visible; z-index: 9000; position: fixed; transition: unset; left: ${left}px; top: ${top}px;` 174 utils.style.cssText = `visibility: visible; z-index: 9000; position: fixed; transition: unset; left: ${left}px; top: ${top}px;`;
174 document.body.appendChild(utils) 175 document.body.appendChild(utils);
175 } 176 };
176 utils.onmouseout = () => { 177 utils.onmouseout = () => {
177 wrapper.appendChild(utils) 178 wrapper.appendChild(utils);
178 utils.removeAttribute('style') 179 utils.removeAttribute("style");
179 } 180 };
180 181
181 // Close button 182 // Close button
182 wrapper.querySelector('#close').onclick = () => { 183 wrapper.querySelector("#close").onclick = () => {
183 wrapper.classList.add('hide') 184 wrapper.classList.add("hide");
184 utils.removeAttribute('style') 185 utils.removeAttribute("style");
185 } 186 };
186 // Plus/Minus font-size of content 187 // Plus/Minus font-size of content
187 wrapper.querySelector('#plus-font-size').onclick = () => { 188 wrapper.querySelector("#plus-font-size").onclick = () => {
188 const fontSize = parseFloat(getComputedStyle(block).fontSize) / 16 189 const fontSize = parseFloat(getComputedStyle(block).fontSize) / 16;
189 block.style.fontSize = `${fontSize + 0.2}rem` 190 block.style.fontSize = `${fontSize + 0.2}rem`;
190 } 191 };
191 wrapper.querySelector('#minus-font-size').onclick = () => { 192 wrapper.querySelector("#minus-font-size").onclick = () => {
192 const fontSize = parseFloat(getComputedStyle(block).fontSize) / 16 193 const fontSize = parseFloat(getComputedStyle(block).fontSize) / 16;
193 block.style.fontSize = `${fontSize - 0.2}rem` 194 block.style.fontSize = `${fontSize - 0.2}rem`;
194 } 195 };
195 }) 196 });
196 } 197 };
197 198
198 leaveHandler = ({ htmlHolder, blocks }) => { 199 leaveHandler = ({ htmlHolder, blocks }) => {
199 const resumeFromDraggable = (block) => { 200 const resumeFromDraggable = block => {
200 const draggableContainer = block.closest('.draggable-block') 201 const draggableContainer = block.closest(".draggable-block");
201 if (!draggableContainer) return 202 if (!draggableContainer) return;
202 htmlHolder.appendChild(block) 203 htmlHolder.appendChild(block);
203 draggableContainer.remove() 204 draggableContainer.remove();
204 } 205 };
205 blocks.forEach(resumeFromDraggable) 206 blocks.forEach(resumeFromDraggable);
206 } 207 };
207} 208}
208