mirror of
https://github.com/Studio-Saelix/sencho.git
synced 2026-08-22 16:16:41 +00:00
feat(ui): make the core stack flow usable on mobile (#1327)
* feat(ui): make the core stack flow usable on mobile Below the md breakpoint the app collapses to a single full-width column: the stack list is full-screen, tapping a stack opens a full-screen detail with a Health / Logs / Compose segmented control (Logs first) and a back button, and a bottom tab bar switches Stacks, Fleet, Schedules, and Settings. Compose is read-only on a phone with a prompt to edit on desktop. Desktop (md and up) is unchanged: the mobile shell is gated behind a useIsMobile hook plus max-md/md variants, and the stack-detail blocks are shared with the desktop two-pane view so it renders identically. Also generalizes the unsaved-changes guard so leaving a dirty editor (back, tab bar, hamburger) prompts before discarding; adds 44px touch targets on list rows, filter chips, and actions; makes log and shell modals full-screen on mobile; and offsets toasts and the deploy pill above the bottom tab bar. * fix(ui): keep mobile nav in sync when opening views from outside the bottom bar On a phone the sidebar activity actions, the node switcher's Manage Nodes, the profile Settings entry, and the dashboard configuration links set the active view without flipping the mobile surface to content, so the user stayed on the stack list and never saw the destination. Route these through the mobile-aware navigation and settings helpers (a no-op on desktop).
This commit is contained in:
@@ -42,7 +42,9 @@ export function SidebarFilterChips({ active, counts, onChange, visible, onToggle
|
||||
type="button"
|
||||
onClick={() => onChange(id)}
|
||||
className={cn(
|
||||
'flex items-center gap-1 min-w-0 overflow-hidden rounded px-1.5 py-0.5 whitespace-nowrap',
|
||||
'flex items-center justify-center gap-1 min-w-0 overflow-hidden rounded px-1.5 py-0.5 whitespace-nowrap',
|
||||
// 44px tap target on touch viewports; desktop density unchanged.
|
||||
'max-md:min-h-11 max-md:py-2',
|
||||
'font-mono text-[10px] tracking-[0.08em] uppercase leading-none',
|
||||
'border transition-colors duration-150',
|
||||
isActive
|
||||
@@ -70,7 +72,7 @@ export function SidebarFilterChips({ active, counts, onChange, visible, onToggle
|
||||
<button
|
||||
type="button"
|
||||
onClick={onToggle}
|
||||
className="px-2 shrink-0 text-stat-icon hover:text-stat-title transition-colors duration-150"
|
||||
className="px-2 max-md:min-h-11 max-md:px-3 shrink-0 text-stat-icon hover:text-stat-title transition-colors duration-150"
|
||||
aria-label={visible ? 'Hide filters' : 'Show filters'}
|
||||
>
|
||||
{visible
|
||||
|
||||
@@ -126,9 +126,10 @@ export function StackRow(props: StackRowProps) {
|
||||
) : null}
|
||||
</span>
|
||||
|
||||
{/* Kebab — always rightmost */}
|
||||
{/* Kebab: always rightmost. Hover-revealed on desktop; always visible on
|
||||
touch viewports where there is no hover. */}
|
||||
<div
|
||||
className="opacity-0 group-hover:opacity-100 transition-opacity flex-shrink-0"
|
||||
className="opacity-0 group-hover:opacity-100 max-md:opacity-100 transition-opacity flex-shrink-0"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
{kebabSlot}
|
||||
|
||||
@@ -59,8 +59,12 @@ export function StackSidebar(props: StackSidebarProps) {
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="w-64 border-r border-glass-border bg-sidebar backdrop-blur-md flex flex-col">
|
||||
<SidebarBrand isDarkMode={isDarkMode} />
|
||||
<div className="w-64 max-md:w-full max-md:flex-1 max-md:min-h-0 max-md:border-r-0 border-r border-glass-border bg-sidebar backdrop-blur-md flex flex-col">
|
||||
{/* The TopBar provides the global chrome on mobile, so the in-sidebar
|
||||
brand row is redundant there and hidden to save vertical space. */}
|
||||
<div className="max-md:hidden">
|
||||
<SidebarBrand isDarkMode={isDarkMode} />
|
||||
</div>
|
||||
<div className="px-4 pt-2 pb-0">{nodeSwitcherSlot}</div>
|
||||
{canCreate && createStackSlot !== null && (
|
||||
<SidebarActions
|
||||
|
||||
@@ -2,6 +2,8 @@ import { cn } from '@/lib/utils';
|
||||
|
||||
export const sidebarRowBase = cn(
|
||||
'relative flex items-center gap-2 w-full px-2 py-1.5 rounded-md mb-0.5',
|
||||
// 44px tap target on touch viewports without changing desktop density.
|
||||
'max-md:min-h-11 max-md:py-2.5',
|
||||
'font-mono text-[13px] text-muted-foreground',
|
||||
'hover:bg-glass-highlight hover:text-foreground',
|
||||
'transition-colors group cursor-pointer',
|
||||
|
||||
Reference in New Issue
Block a user