aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDanilo M. <danix@danix.xyz>2026-11-09 20:04:06 +0100
committerDanilo M. <danix@danix.xyz>2026-11-09 20:04:06 +0100
commitc47e0f22207302e4dcad9b8480e033fbe85dccd5 (patch)
treee34e77c8cc018f78bc9bf5b2c70d07e05d07725c
parentd107596f6c277d6aa4616616cc4fe176f339a8f5 (diff)
downloaddeck-it-c47e0f22207302e4dcad9b8480e033fbe85dccd5.tar.gz
deck-it-c47e0f22207302e4dcad9b8480e033fbe85dccd5.zip
feat: center the tabs and swipe to switch them
-rw-r--r--static/app.js32
-rw-r--r--static/deck.css18
-rw-r--r--static/index.html1
-rw-r--r--tests/test_frontend.py2
4 files changed, 28 insertions, 25 deletions
diff --git a/static/app.js b/static/app.js
index d125d69..296166a 100644
--- a/static/app.js
+++ b/static/app.js
@@ -186,15 +186,33 @@ async function boot() {
showTab(tabs[saved] ? saved : "Dashboard");
}
-const fullscreenButton = document.getElementById("fullscreen");
-fullscreenButton?.addEventListener("click", async () => {
- try {
- if (document.fullscreenElement) await document.exitFullscreen();
- else await document.documentElement.requestFullscreen();
- } catch {
- // the browser refused; nothing to do
+function stepTab(dir) {
+ const names = [...tabBar.children].map((button) => button.textContent);
+ const at = names.findIndex((name) => !tabs[name]?.section.hidden);
+ const next = at + dir;
+ if (at !== -1 && next >= 0 && next < names.length) showTab(names[next]);
+}
+
+// Swipe the content sideways to change tab; gestures on sliders and buttons
+// (including the card drag handles) are theirs, not the shell's.
+let swipe = null;
+tabBody.addEventListener("pointerdown", (e) => {
+ if (e.pointerType === "mouse" && e.buttons !== 1) return;
+ if (e.target.closest("input, select, textarea, button")) {
+ swipe = null;
+ return;
}
+ swipe = { x: e.clientX, y: e.clientY };
+});
+tabBody.addEventListener("pointerup", (e) => {
+ if (!swipe) return;
+ const dx = e.clientX - swipe.x;
+ const dy = e.clientY - swipe.y;
+ swipe = null;
+ if (Math.abs(dx) < 70 || Math.abs(dx) < Math.abs(dy) * 1.5) return;
+ stepTab(dx < 0 ? 1 : -1);
});
+tabBody.addEventListener("pointercancel", () => { swipe = null; });
document.addEventListener("visibilitychange", () => {
if (document.visibilityState === "visible") refreshers.forEach((fn) => fn());
diff --git a/static/deck.css b/static/deck.css
index 5bd8a5d..ee2be1f 100644
--- a/static/deck.css
+++ b/static/deck.css
@@ -19,24 +19,10 @@ body {
.topbar {
flex: none;
display: flex;
- align-items: center;
- gap: 8px;
+ justify-content: center;
padding: 8px 14px 0;
}
-.tabs { flex: 1; display: flex; gap: 6px; min-width: 0; }
-.fs {
- flex: none;
- font: inherit;
- font-size: 18px;
- line-height: 1;
- color: var(--udt-subtext0);
- background: transparent;
- border: 1px solid transparent;
- border-radius: 10px;
- padding: 6px 12px;
- cursor: pointer;
-}
-.fs:hover { background: var(--udt-surface1); color: var(--udt-text); }
+.tabs { display: flex; gap: 6px; }
.tabs button {
font: inherit;
color: var(--udt-subtext0);
diff --git a/static/index.html b/static/index.html
index 434e496..5a4ca3e 100644
--- a/static/index.html
+++ b/static/index.html
@@ -19,7 +19,6 @@
<body>
<header class="topbar">
<nav id="tabs" class="tabs"></nav>
- <button id="fullscreen" class="fs" aria-label="Toggle fullscreen">⛶</button>
</header>
<div id="tab-body"></div>
<script src="vendor/gridstack/gridstack-all.js"></script>
diff --git a/tests/test_frontend.py b/tests/test_frontend.py
index 1111f89..0889c5b 100644
--- a/tests/test_frontend.py
+++ b/tests/test_frontend.py
@@ -94,7 +94,7 @@ class Frontend(unittest.TestCase):
def test_tabbed_shell(self):
self.assertIn(b'id="tabs"', self.get("/")[1])
self.assertIn(b'id="tab-body"', self.get("/")[1])
- self.assertIn(b'id="fullscreen"', self.get("/")[1])
+ self.assertIn(b"stepTab", self.get("/app.js")[1])
app = self.get("/app.js")[1]
for part in (b"deckit.tab", b"ensureTab", b"showTab"):
self.assertIn(part, app)