taskbar handles many open windows: shrink buttons, scroll on overflow, keep active in view
This commit is contained in:
parent
7638fac97b
commit
338146671b
3 changed files with 54 additions and 4 deletions
|
|
@ -26,7 +26,9 @@ heatmap**, a gym SBD panel and links to friends' sites.
|
|||
- **Draggable desktop icons** — free placement that snaps to a measured grid, one icon per
|
||||
cell, with a follow-the-cursor drag ghost.
|
||||
- **Multi-window manager** — draggable, resizable, minimizable, maximizable, focus/z-order
|
||||
and animated open/close (with reduced-motion support).
|
||||
and animated open/close (with reduced-motion support). The taskbar shrinks its window
|
||||
buttons as more apps open and scrolls when they still don't fit, so every open app stays
|
||||
reachable.
|
||||
- **Working terminal app** — command input with history (`↑`/`↓`, `Ctrl+L` to clear) and a
|
||||
hidden `ssh` easter egg.
|
||||
- **Live service status** — each self-hosted service is health-checked through the site's
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
"use client";
|
||||
|
||||
import { useRef, useState, type ReactNode } from "react";
|
||||
import { useEffect, useRef, useState, type ReactNode } from "react";
|
||||
import Image from "next/image";
|
||||
import { APPS, APP_BY_ID, createWebsiteApp, type AppDef } from "./apps";
|
||||
import WinWindow, { type WinState } from "./WinWindow";
|
||||
|
|
@ -89,9 +89,25 @@ export default function WindowsDesktop() {
|
|||
const friendItems = FRIEND_SHORTCUTS;
|
||||
|
||||
const desktopRef = useRef<HTMLDivElement>(null);
|
||||
const tbRef = useRef<HTMLDivElement>(null);
|
||||
const { positions, dragId, onPointerDown, consumeDrag } =
|
||||
useDesktopIcons(desktopRef, ICON_KEYS);
|
||||
|
||||
// When the taskbar buttons overflow, keep the focused window's button visible
|
||||
// so opening an app doesn't leave its button off the (scrollable) right edge.
|
||||
useEffect(() => {
|
||||
const bar = tbRef.current;
|
||||
const active = bar?.querySelector<HTMLElement>(".win-tb-btn.active");
|
||||
if (!bar || !active) return;
|
||||
const barRect = bar.getBoundingClientRect();
|
||||
const btnRect = active.getBoundingClientRect();
|
||||
if (btnRect.left < barRect.left) {
|
||||
bar.scrollLeft += btnRect.left - barRect.left;
|
||||
} else if (btnRect.right > barRect.right) {
|
||||
bar.scrollLeft += btnRect.right - barRect.right;
|
||||
}
|
||||
}, [activeId]);
|
||||
|
||||
const [dynamicApps, setDynamicApps] = useState<Record<string, AppDef>>({});
|
||||
const getApp = (id: string): AppDef | undefined => APP_BY_ID[id] ?? dynamicApps[id];
|
||||
|
||||
|
|
@ -452,7 +468,16 @@ export default function WindowsDesktop() {
|
|||
</span>
|
||||
</button>
|
||||
|
||||
<div className="win-tb-buttons">
|
||||
<div
|
||||
className="win-tb-buttons"
|
||||
ref={tbRef}
|
||||
onWheel={(e) => {
|
||||
const bar = tbRef.current;
|
||||
// Translate a vertical mouse wheel into horizontal scrolling while
|
||||
// the button strip overflows, so every open app stays reachable.
|
||||
if (bar && bar.scrollWidth > bar.clientWidth) bar.scrollLeft += e.deltaY;
|
||||
}}
|
||||
>
|
||||
{wins.map((w) => {
|
||||
const app = getApp(w.appId) ?? APP_BY_ID.welcome;
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -1106,13 +1106,35 @@ a.win-icon {
|
|||
gap: 2px;
|
||||
padding: 4px 4px;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
/* Grow into the free space, then shrink the buttons, then scroll once even
|
||||
the shrunken buttons no longer fit (see .win-tb-btn min-width). */
|
||||
flex: 1 1 auto;
|
||||
overflow-x: auto;
|
||||
overflow-y: hidden;
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: rgba(255, 255, 255, 0.28) transparent;
|
||||
}
|
||||
.win-tb-buttons::-webkit-scrollbar {
|
||||
height: 4px;
|
||||
}
|
||||
.win-tb-buttons::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
}
|
||||
.win-tb-buttons::-webkit-scrollbar-thumb {
|
||||
background: rgba(255, 255, 255, 0.28);
|
||||
border-radius: 2px;
|
||||
}
|
||||
.win-tb-buttons::-webkit-scrollbar-thumb:hover {
|
||||
background: rgba(255, 255, 255, 0.45);
|
||||
}
|
||||
.win-tb-btn {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 7px;
|
||||
flex: 0 1 auto;
|
||||
min-width: 0;
|
||||
max-width: 172px;
|
||||
padding: 0 10px;
|
||||
border: 1px solid transparent;
|
||||
|
|
@ -1182,6 +1204,7 @@ a.win-icon {
|
|||
}
|
||||
.win-tb-btn-label {
|
||||
font-size: 0.75rem;
|
||||
min-width: 0;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue