Skip to content

Commit 7940a36

Browse files
author
mirkobrombin
committed
feat: enhance modal functionality with type support and minimize feature
1 parent f937e01 commit 7940a36

File tree

3 files changed

+116
-41
lines changed

3 files changed

+116
-41
lines changed

internal/dashboard/static/js/logs.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ export default {
115115
{
116116
id: "closeBtn",
117117
text: "Close",
118+
icon: "close",
118119
onClick: () => Modal.hide("logModal"),
119120
},
120121
],

internal/dashboard/static/js/modal.js

Lines changed: 113 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,24 @@
1+
let minimizedModalsByType = {};
2+
let modalCounter = 0;
3+
14
export default {
2-
show({ id = "custom-modal", title = "", content = "", buttons = [] }) {
5+
show({
6+
id = "custom-modal",
7+
title = "",
8+
content = "",
9+
buttons = [],
10+
type = "default",
11+
}) {
312
this.setNoSelect();
4-
5-
let modal = document.getElementById(id);
6-
if (!modal) {
7-
modal = document.createElement("div");
8-
modal.id = id;
9-
modal.className =
10-
"fixed bottom-0 left-0 w-full bg-white shadow-xl rounded-t-xl transform translate-y-full transition-transform duration-300 z-50";
11-
modal.innerHTML = `
13+
if (document.getElementById(id)) {
14+
id = id + "-" + ++modalCounter;
15+
}
16+
let modal = document.createElement("div");
17+
modal.id = id;
18+
modal.dataset.type = type;
19+
modal.className =
20+
"fixed bottom-0 left-0 w-full bg-white shadow-xl rounded-t-xl transform translate-y-full transition-transform duration-300 z-50";
21+
modal.innerHTML = `
1222
<div class="cursor-pointer w-full bg-gray-300 h-1.5 rounded-full mt-2 mx-auto max-w-16"></div>
1323
<div class="mx-auto p-4 border-b">
1424
<div class="max-w-7xl mx-auto flex items-center justify-between">
@@ -20,25 +30,21 @@ export default {
2030
${content}
2131
</div>
2232
`;
23-
document.body.appendChild(modal);
24-
25-
this.updateButtons(id, buttons);
26-
27-
this.enableDrag(id);
28-
29-
setTimeout(() => {
30-
modal.style.transform = "translateY(0)";
31-
}, 10);
32-
}
33+
document.body.appendChild(modal);
34+
this.updateButtons(id, buttons);
35+
this.enableDrag(id);
36+
setTimeout(() => {
37+
modal.style.transform = "translateY(0px)";
38+
}, 10);
3339
},
3440

3541
hide(id = "custom-modal") {
3642
this.removeNoSelect();
37-
3843
const modal = document.getElementById(id);
3944
if (modal) {
4045
modal.style.transform = "translateY(100%)";
4146
setTimeout(() => modal.remove(), 300);
47+
this._removeMinimized(modal);
4248
}
4349
},
4450

@@ -50,15 +56,21 @@ export default {
5056
updateButtons(id, buttons) {
5157
const buttonsContainer = document.getElementById(`${id}-buttons`);
5258
if (!buttonsContainer) return;
53-
5459
buttonsContainer.innerHTML = "";
55-
buttons.forEach(({ id, text, onClick, hidden = false }) => {
60+
buttons.forEach(({ id, text, icon, onClick, hidden = false }) => {
5661
const btn = document.createElement("button");
5762
btn.id = `${id}`;
58-
btn.textContent = text;
59-
btn.className = `px-4 py-1 rounded ${
60-
hidden ? "hidden" : ""
61-
} bg-blue-500 text-white hover:bg-blue-600`;
63+
if (icon) {
64+
btn.innerHTML = `<span class="material-symbols-outlined" aria-label="${text}">${icon}</span>`;
65+
btn.className = `px-4 py-1 rounded ${
66+
hidden ? "hidden" : ""
67+
} text-gray-700`;
68+
} else {
69+
btn.textContent = text;
70+
btn.className = `px-4 py-1 rounded ${
71+
hidden ? "hidden" : ""
72+
} bg-blue-500 text-white hover:bg-blue-600`;
73+
}
6274
btn.addEventListener("click", onClick);
6375
buttonsContainer.appendChild(btn);
6476
});
@@ -74,39 +86,99 @@ export default {
7486
isDragging = false;
7587
let modal = document.getElementById(id);
7688
let pill = modal.querySelector(".cursor-pointer");
77-
78-
let initialTransform = 100;
79-
let currentTransform = initialTransform;
80-
89+
let initialTransform = 0;
90+
let currentTransform = 0;
91+
const anchorThreshold = 150;
8192
pill.addEventListener("mousedown", (e) => {
8293
isDragging = true;
8394
startY = e.clientY;
84-
initialTransform =
85-
parseFloat(getComputedStyle(modal).transform.split(",")[5]) || 0;
95+
const transform = getComputedStyle(modal).transform;
96+
if (transform !== "none") {
97+
const matrix = transform.match(/matrix.*\((.+)\)/)[1].split(", ");
98+
initialTransform = parseFloat(matrix[5]);
99+
} else {
100+
initialTransform = 0;
101+
}
86102
modal.style.transition = "none";
87103
});
88-
89104
document.addEventListener("mousemove", (e) => {
90105
if (!isDragging) return;
91106
let diff = e.clientY - startY;
92-
let newTransform = Math.min(Math.max(initialTransform + diff, 0), 100);
93-
modal.style.transform = `translateY(${newTransform}%)`;
107+
let newTransform = initialTransform + diff;
108+
newTransform = Math.max(newTransform, 0);
109+
modal.style.transform = `translateY(${newTransform}px)`;
94110
currentTransform = newTransform;
95111
});
96-
97112
document.addEventListener("mouseup", () => {
98113
if (!isDragging) return;
99114
isDragging = false;
100-
modal.style.transition = "transform 0.3s ease";
101-
if (currentTransform > 70) {
102-
modal.style.transform = "translateY(100%)";
103-
setTimeout(() => modal.remove(), 300);
115+
modal.style.transition = "all 0.3s ease";
116+
if (currentTransform >= anchorThreshold) {
117+
this.minimizeModal(modal);
104118
} else {
105-
modal.style.transform = "translateY(0)";
119+
modal.style.transform = "translateY(0px)";
106120
}
107121
});
108122
},
109123

124+
minimizeModal(modal) {
125+
modal.style.transform = "translateY(0px)";
126+
modal.style.height = "74px";
127+
modal.style.width = "200px";
128+
modal.style.bottom = "10px";
129+
modal.style.zIndex = "0";
130+
let type = modal.dataset.type || "default";
131+
if (!minimizedModalsByType[type]) {
132+
minimizedModalsByType[type] = [];
133+
}
134+
const arr = minimizedModalsByType[type];
135+
const index = arr.length;
136+
arr.push(modal);
137+
const spacing = 10;
138+
const modalWidth = 200;
139+
let left = 10 + index * (modalWidth + spacing);
140+
const maxLeft = window.innerWidth - modalWidth - 10;
141+
if (left > maxLeft) left = maxLeft;
142+
modal.style.left = `${left}px`;
143+
modal.classList.add("rounded-xl");
144+
const title = modal.querySelector(".font-semibold");
145+
if (title) {
146+
title.classList.add("truncate", "text-sm");
147+
}
148+
const restoreHandler = (e) => {
149+
modal.style.transition = "all 0.3s ease";
150+
modal.style.height = "";
151+
modal.style.width = "100%";
152+
modal.style.left = "0";
153+
modal.style.zIndex = "50";
154+
modal.classList.remove("rounded-xl");
155+
if (title) {
156+
title.classList.remove("truncate", "text-sm");
157+
}
158+
modal.removeEventListener("click", restoreHandler);
159+
this._removeMinimized(modal);
160+
};
161+
modal.addEventListener("click", restoreHandler);
162+
modal._restoreHandler = restoreHandler;
163+
},
164+
165+
_removeMinimized(modal) {
166+
let type = modal.dataset.type || "default";
167+
if (minimizedModalsByType[type]) {
168+
minimizedModalsByType[type] = minimizedModalsByType[type].filter(
169+
(m) => m !== modal
170+
);
171+
minimizedModalsByType[type].forEach((m, i) => {
172+
const spacing = 10;
173+
const modalWidth = 200;
174+
let left = 10 + i * (modalWidth + spacing);
175+
const maxLeft = window.innerWidth - modalWidth - 10;
176+
if (left > maxLeft) left = maxLeft;
177+
m.style.left = `${left}px`;
178+
});
179+
}
180+
},
181+
110182
setNoSelect() {
111183
document.body.style.userSelect = "none";
112184
},

internal/dashboard/static/js/sites.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ export default {
3131
{
3232
id: "edit-json-btn",
3333
text: "Edit",
34+
icon: "edit",
3435
onClick: () => {
3536
Modal.setTitle("site-json-modal", `Editing ${domain}'s JSON`);
3637
Modal.toggleButton("site-json-modal", "edit-json-btn", false);
@@ -44,6 +45,7 @@ export default {
4445
id: "save-json-btn",
4546
text: "Save Changes",
4647
hidden: true,
48+
icon: "save",
4749
onClick: async () => {
4850
const newConfig = document.getElementById("json-editor").value;
4951
await fetch(`/api/sites/${domain}`, {

0 commit comments

Comments
 (0)