Port floorplan item thumbnails, full mobile editor UI, and mobile polish from the private monorepo into the public editor. Monorepo PR #297 (feat/item-admin-advanced): - Add optional floorPlanUrl to item asset schema (core) - Render 2D floor-plan images inside item footprints on floorplan - Improve slider drag with modifier key re-anchoring Monorepo PR #302 (feat/mobile-ui): - Mobile editor layout with draggable bottom sheet - Mobile tab bar, selection bar, and panel sheet - Mobile-aware panel manager and panel wrapper - useIsMobile rewrite (useSyncExternalStore, SSR-safe) - Camera actions hideOrbit prop - GridSnapControl and SecondaryToggles exports - Action menu hides on mobile contextual tabs - SidebarTab extended with mobileDefaultSnap/mobileIcon Monorepo PR #306 (feat/mobile-ui-polish): - Touch gesture mapping for camera controls (one/two/three finger) - Snap ratio constants for bottom sheet - Thumbnail generator WebGL2 fallback (bottom-up row flip) - ErrorBoundary scope logging, viewer scene wrap - GPUDeviceWatcher enhanced logging and uncaptured error handler - WebGPURenderer init error handling and diagnostics - Post-processing log improvements - MergedOutlineNode WebGL2 FBO corruption fix Excluded: apps/community/*, apps/editor/*, packages/community-*, .cursor/*, .env.example (monorepo-only files) Co-authored-by: Pascal <open@pascal.app>
47 lines
1.5 KiB
TypeScript
47 lines
1.5 KiB
TypeScript
'use client'
|
||
|
||
import { cn } from './../../../lib/utils'
|
||
import type { SidebarTab } from './tab-bar'
|
||
|
||
interface MobileTabBarProps {
|
||
tabs: SidebarTab[]
|
||
activeTab: string
|
||
onTabPress: (id: string) => void
|
||
}
|
||
|
||
export function MobileTabBar({ tabs, activeTab, onTabPress }: MobileTabBarProps) {
|
||
return (
|
||
<div
|
||
className="z-50 flex h-14 shrink-0 border-border/50 border-t bg-sidebar text-sidebar-foreground"
|
||
style={{
|
||
// Cap the safe-area inset — iOS Chrome can report its bottom UI bar
|
||
// (50–100px) as part of the safe area which would balloon the tab bar.
|
||
// 34px matches the iPhone home-indicator height (the typical max).
|
||
paddingBottom: 'min(env(safe-area-inset-bottom, 0px), 34px)',
|
||
}}
|
||
>
|
||
{tabs.map((tab) => {
|
||
const isActive = activeTab === tab.id
|
||
return (
|
||
<button
|
||
className={cn(
|
||
'flex flex-1 flex-col items-center justify-center gap-0.5 text-xs transition-colors',
|
||
isActive ? 'text-foreground' : 'text-muted-foreground',
|
||
)}
|
||
key={tab.id}
|
||
onClick={() => onTabPress(tab.id)}
|
||
type="button"
|
||
>
|
||
{tab.mobileIcon ? (
|
||
<span className={cn('flex h-5 w-5 items-center justify-center')}>
|
||
{tab.mobileIcon}
|
||
</span>
|
||
) : null}
|
||
<span className="font-medium">{tab.label}</span>
|
||
</button>
|
||
)
|
||
})}
|
||
</div>
|
||
)
|
||
}
|