diff options
author | Hsieh Chin Fan <pham@topo.tw> | 2024-09-21 16:40:22 +0800 |
---|---|---|
committer | Hsieh Chin Fan <pham@topo.tw> | 2024-09-21 17:08:05 +0800 |
commit | cbe40ac1128eedcda30812285cbec003acb8adc1 (patch) | |
tree | 0b1765b9cceaee4c7532282cf733cb5f95ecb18a /src/Layout.mjs | |
parent | e015aeb2c13903871da306d3aa840c25c7a3597f (diff) |
refactor: layout class
* put class Layout and OverlayLayout together
* apply extend on OverlayLayout
* rename layouts: "none" -> "normal", "side" -> "side-by-side"
Diffstat (limited to 'src/Layout.mjs')
-rw-r--r-- | src/Layout.mjs | 117 |
1 files changed, 117 insertions, 0 deletions
diff --git a/src/Layout.mjs b/src/Layout.mjs new file mode 100644 index 0000000..20e8a23 --- /dev/null +++ b/src/Layout.mjs | |||
@@ -0,0 +1,117 @@ | |||
1 | import PlainDraggable from 'plain-draggable' | ||
2 | import { onRemove } from './utils' | ||
3 | |||
4 | export class Layout { | ||
5 | constructor(options = {}) { | ||
6 | if (!options.name) throw Error("Layout name is not given") | ||
7 | this.name = options.name | ||
8 | this.enterHandler = options.enterHandler | ||
9 | this.leaveHandler = options.leaveHandler | ||
10 | } | ||
11 | valueOf = () => this.name | ||
12 | } | ||
13 | |||
14 | export class OverlayLayout extends Layout { | ||
15 | name = "overlay" | ||
16 | |||
17 | enterHandler = (dumbymap) => { | ||
18 | const container = dumbymap.htmlHolder | ||
19 | const moveIntoDraggable = (block) => { | ||
20 | // Create draggable block | ||
21 | const draggableBlock = document.createElement('div') | ||
22 | draggableBlock.classList.add('draggable-block') | ||
23 | draggableBlock.innerHTML = ` | ||
24 | <div class="draggable"> | ||
25 | <div class="handle">\u2630</div> | ||
26 | </div> | ||
27 | <div class="utils"> | ||
28 | <div id="plus-font-size" ">\u2795</div> | ||
29 | <div id="minus-font-size">\u2796</div> | ||
30 | </div> | ||
31 | ` | ||
32 | |||
33 | // Add draggable part | ||
34 | const draggablePart = draggableBlock.querySelector('.draggable') | ||
35 | draggablePart.title = 'Use middle-click to remove block' | ||
36 | draggablePart.onmouseup = (e) => { | ||
37 | if (e.button === 1) { | ||
38 | // Hide block with middle click | ||
39 | draggableBlock.style.display = "none"; | ||
40 | } | ||
41 | } | ||
42 | |||
43 | // Set elements | ||
44 | draggableBlock.appendChild(draggablePart) | ||
45 | draggableBlock.appendChild(block) | ||
46 | container.appendChild(draggableBlock) | ||
47 | |||
48 | // Add draggable instance | ||
49 | const draggableInstance = new PlainDraggable(draggableBlock, { | ||
50 | handle: draggablePart, | ||
51 | snap: { x: { step: 20 }, y: { step: 20 } }, | ||
52 | }) | ||
53 | |||
54 | // Plus/Minus font-size of content | ||
55 | const plusButton = draggableBlock.querySelector('#plus-font-size') | ||
56 | plusButton.onclick = () => { | ||
57 | const fontSize = parseFloat(getComputedStyle(block).fontSize) / 16 | ||
58 | block.style.fontSize = `${fontSize + 0.1}rem` | ||
59 | } | ||
60 | const minusButton = draggableBlock.querySelector('#minus-font-size') | ||
61 | minusButton.onclick = () => { | ||
62 | const fontSize = parseFloat(getComputedStyle(block).fontSize) / 16 | ||
63 | block.style.fontSize = `${fontSize - 0.1}rem` | ||
64 | } | ||
65 | draggableInstance.onDragStart = () => { | ||
66 | plusButton.style.opacity = '0' | ||
67 | minusButton.style.opacity = '0' | ||
68 | } | ||
69 | draggableInstance.onDragEnd = () => { | ||
70 | plusButton.style = '' | ||
71 | minusButton.style = '' | ||
72 | } | ||
73 | |||
74 | // Reposition draggable instance when resized | ||
75 | new ResizeObserver(() => { | ||
76 | try { | ||
77 | draggableInstance.position(); | ||
78 | } catch (_) { | ||
79 | null | ||
80 | } | ||
81 | }).observe(draggableBlock); | ||
82 | |||
83 | // Callback for remove | ||
84 | onRemove(draggableBlock, () => { | ||
85 | draggableInstance.remove() | ||
86 | }) | ||
87 | |||
88 | return draggableInstance | ||
89 | } | ||
90 | |||
91 | // Create draggable blocks and set each position by previous one | ||
92 | let [x, y] = [0, 0] | ||
93 | dumbymap.blocks.map(moveIntoDraggable) | ||
94 | .forEach(draggable => { | ||
95 | draggable.left = x | ||
96 | draggable.top = y | ||
97 | const rect = draggable.element.getBoundingClientRect() | ||
98 | x += rect.width + 30 | ||
99 | if (x > window.innerWidth) { | ||
100 | y += 200 | ||
101 | x = x % window.innerWidth | ||
102 | } | ||
103 | }) | ||
104 | } | ||
105 | leaveHandler = (dumbymap) => { | ||
106 | const container = dumbymap.htmlHolder | ||
107 | const resumeFromDraggable = (block) => { | ||
108 | const draggableContainer = block.closest('.draggable-block') | ||
109 | if (!draggableContainer) return | ||
110 | container.appendChild(block) | ||
111 | block.removeAttribute('style') | ||
112 | draggableContainer.remove() | ||
113 | } | ||
114 | dumbymap.blocks.forEach(resumeFromDraggable) | ||
115 | } | ||
116 | } | ||
117 | |||