Monorepo for Tangled
1{{ define "fragments/resizable" }}
2 <script>
3 class ResizablePanel {
4 constructor(resizerElement) {
5 this.resizer = resizerElement;
6 this.isResizing = false;
7 this.type = resizerElement.dataset.resizer;
8 this.targetId = resizerElement.dataset.target;
9 this.target = document.getElementById(this.targetId);
10 this.min = parseInt(resizerElement.dataset.min) || 100;
11 this.max = parseInt(resizerElement.dataset.max) || Infinity;
12
13 this.direction = resizerElement.dataset.direction || 'before'; // 'before' or 'after'
14
15 this.handleMouseDown = this.handleMouseDown.bind(this);
16 this.handleMouseMove = this.handleMouseMove.bind(this);
17 this.handleMouseUp = this.handleMouseUp.bind(this);
18
19 this.init();
20 }
21
22 init() {
23 this.resizer.addEventListener('mousedown', this.handleMouseDown);
24 }
25
26 handleMouseDown(e) {
27 e.preventDefault();
28 this.isResizing = true;
29 this.resizer.classList.add('resizing');
30 document.body.style.cursor = this.type === 'vertical' ? 'col-resize' : 'row-resize';
31 document.body.style.userSelect = 'none';
32
33 this.startX = e.clientX;
34 this.startY = e.clientY;
35 this.startWidth = this.target.offsetWidth;
36 this.startHeight = this.target.offsetHeight;
37
38 document.addEventListener('mousemove', this.handleMouseMove);
39 document.addEventListener('mouseup', this.handleMouseUp);
40 }
41
42 handleMouseMove(e) {
43 if (!this.isResizing) return;
44
45 if (this.type === 'vertical') {
46 let newWidth;
47
48 if (this.direction === 'after') {
49 const deltaX = this.startX - e.clientX;
50 newWidth = this.startWidth + deltaX;
51 } else {
52 const deltaX = e.clientX - this.startX;
53 newWidth = this.startWidth + deltaX;
54 }
55
56 if (newWidth >= this.min && newWidth <= this.max) {
57 this.target.style.width = newWidth + 'px';
58 this.target.style.flexShrink = '0';
59 }
60 } else {
61 let newHeight;
62
63 if (this.direction === 'after') {
64 const deltaY = this.startY - e.clientY;
65 newHeight = this.startHeight + deltaY;
66 } else {
67 const deltaY = e.clientY - this.startY;
68 newHeight = this.startHeight + deltaY;
69 }
70
71 if (newHeight >= this.min && newHeight <= this.max) {
72 this.target.style.height = newHeight + 'px';
73 }
74 }
75 }
76
77 handleMouseUp() {
78 if (!this.isResizing) return;
79
80 this.isResizing = false;
81 this.resizer.classList.remove('resizing');
82 document.body.style.cursor = '';
83 document.body.style.userSelect = '';
84
85 document.removeEventListener('mousemove', this.handleMouseMove);
86 document.removeEventListener('mouseup', this.handleMouseUp);
87 }
88
89 destroy() {
90 this.resizer.removeEventListener('mousedown', this.handleMouseDown);
91 document.removeEventListener('mousemove', this.handleMouseMove);
92 document.removeEventListener('mouseup', this.handleMouseUp);
93 }
94 }
95
96 function initializeResizers() {
97 const resizers = document.querySelectorAll('[data-resizer]');
98 const instances = [];
99
100 resizers.forEach(resizer => {
101 instances.push(new ResizablePanel(resizer));
102 });
103
104 return instances;
105 }
106
107 if (document.readyState === 'loading') {
108 document.addEventListener('DOMContentLoaded', initializeResizers);
109 } else {
110 initializeResizers();
111 }
112 </script>
113{{ end }}