fix z-index viewer
This commit is contained in:
@@ -1,9 +1,26 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import { type AnyNode, type AnyNodeId, type BuildingNode, type LevelNode, type ZoneNode, useScene } from '@pascal-app/core'
|
import {
|
||||||
|
type AnyNode,
|
||||||
|
type AnyNodeId,
|
||||||
|
type BuildingNode,
|
||||||
|
type LevelNode,
|
||||||
|
useScene,
|
||||||
|
type ZoneNode,
|
||||||
|
} from '@pascal-app/core'
|
||||||
import { useViewer } from '@pascal-app/viewer'
|
import { useViewer } from '@pascal-app/viewer'
|
||||||
|
import {
|
||||||
|
ArrowLeft,
|
||||||
|
Box,
|
||||||
|
ChevronRight,
|
||||||
|
Diamond,
|
||||||
|
Eye,
|
||||||
|
EyeOff,
|
||||||
|
Image,
|
||||||
|
Layers,
|
||||||
|
Layers2,
|
||||||
|
} from 'lucide-react'
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
import { ArrowLeft, Box, ChevronRight, Diamond, Eye, EyeOff, Image, Layers, Layers2 } from 'lucide-react'
|
|
||||||
import type { ProjectOwner } from '@/features/community/lib/projects/types'
|
import type { ProjectOwner } from '@/features/community/lib/projects/types'
|
||||||
|
|
||||||
const getNodeName = (node: AnyNode): string => {
|
const getNodeName = (node: AnyNode): string => {
|
||||||
@@ -23,7 +40,12 @@ interface ViewerOverlayProps {
|
|||||||
canShowGuides?: boolean
|
canShowGuides?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ViewerOverlay = ({ projectName, owner, canShowScans = true, canShowGuides = true }: ViewerOverlayProps) => {
|
export const ViewerOverlay = ({
|
||||||
|
projectName,
|
||||||
|
owner,
|
||||||
|
canShowScans = true,
|
||||||
|
canShowGuides = true,
|
||||||
|
}: ViewerOverlayProps) => {
|
||||||
const selection = useViewer((s) => s.selection)
|
const selection = useViewer((s) => s.selection)
|
||||||
const nodes = useScene((s) => s.nodes)
|
const nodes = useScene((s) => s.nodes)
|
||||||
const showScans = useViewer((s) => s.showScans)
|
const showScans = useViewer((s) => s.showScans)
|
||||||
@@ -32,20 +54,24 @@ export const ViewerOverlay = ({ projectName, owner, canShowScans = true, canShow
|
|||||||
const levelMode = useViewer((s) => s.levelMode)
|
const levelMode = useViewer((s) => s.levelMode)
|
||||||
const wallMode = useViewer((s) => s.wallMode)
|
const wallMode = useViewer((s) => s.wallMode)
|
||||||
|
|
||||||
const building = selection.buildingId ? (nodes[selection.buildingId] as BuildingNode | undefined) : null
|
const building = selection.buildingId
|
||||||
|
? (nodes[selection.buildingId] as BuildingNode | undefined)
|
||||||
|
: null
|
||||||
const level = selection.levelId ? (nodes[selection.levelId] as LevelNode | undefined) : null
|
const level = selection.levelId ? (nodes[selection.levelId] as LevelNode | undefined) : null
|
||||||
const zone = selection.zoneId ? (nodes[selection.zoneId] as ZoneNode | undefined) : null
|
const zone = selection.zoneId ? (nodes[selection.zoneId] as ZoneNode | undefined) : null
|
||||||
|
|
||||||
// Get the first selected item (if any)
|
// Get the first selected item (if any)
|
||||||
const selectedNode = selection.selectedIds.length > 0
|
const selectedNode =
|
||||||
? (nodes[selection.selectedIds[0] as AnyNodeId] as AnyNode | undefined)
|
selection.selectedIds.length > 0
|
||||||
: null
|
? (nodes[selection.selectedIds[0] as AnyNodeId] as AnyNode | undefined)
|
||||||
|
: null
|
||||||
|
|
||||||
// Get all levels for the selected building
|
// Get all levels for the selected building
|
||||||
const levels = building?.children
|
const levels =
|
||||||
.map((id) => nodes[id as AnyNodeId] as LevelNode | undefined)
|
building?.children
|
||||||
.filter((n): n is LevelNode => n?.type === 'level')
|
.map((id) => nodes[id as AnyNodeId] as LevelNode | undefined)
|
||||||
.sort((a, b) => a.level - b.level) ?? []
|
.filter((n): n is LevelNode => n?.type === 'level')
|
||||||
|
.sort((a, b) => a.level - b.level) ?? []
|
||||||
|
|
||||||
const handleLevelClick = (levelId: LevelNode['id']) => {
|
const handleLevelClick = (levelId: LevelNode['id']) => {
|
||||||
// When switching levels, deselect zone and items
|
// When switching levels, deselect zone and items
|
||||||
@@ -68,219 +94,249 @@ export const ViewerOverlay = ({ projectName, owner, canShowScans = true, canShow
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{/* Unified top-left card */}
|
{/* Unified top-left card */}
|
||||||
<div className="absolute top-4 left-4 z-10 flex flex-col gap-3">
|
<div className="absolute top-4 left-4 z-20 flex flex-col gap-3">
|
||||||
<div className="bg-white/80 backdrop-blur-sm rounded-lg rounded-smooth shadow-[0_1px_4px_rgba(0,0,0,0.06),0_0_0_1px_rgba(0,0,0,0.03)] overflow-hidden">
|
<div className="bg-white/80 backdrop-blur-sm rounded-lg rounded-smooth shadow-[0_1px_4px_rgba(0,0,0,0.06),0_0_0_1px_rgba(0,0,0,0.03)] overflow-hidden">
|
||||||
{/* Project info + back */}
|
{/* Project info + back */}
|
||||||
<div className="flex items-center gap-3 px-3 py-2">
|
<div className="flex items-center gap-3 px-3 py-2">
|
||||||
<Link
|
<Link
|
||||||
href="/"
|
href="/"
|
||||||
className="flex h-7 w-7 shrink-0 items-center justify-center rounded-md hover:bg-neutral-100 transition-colors"
|
className="flex h-7 w-7 shrink-0 items-center justify-center rounded-md hover:bg-neutral-100 transition-colors"
|
||||||
>
|
>
|
||||||
<ArrowLeft className="h-4 w-4 text-neutral-500" />
|
<ArrowLeft className="h-4 w-4 text-neutral-500" />
|
||||||
</Link>
|
</Link>
|
||||||
<div className="min-w-0">
|
<div className="min-w-0">
|
||||||
<div className="text-sm font-medium text-neutral-800 truncate">
|
<div className="text-sm font-medium text-neutral-800 truncate">
|
||||||
{projectName || 'Untitled'}
|
{projectName || 'Untitled'}
|
||||||
|
</div>
|
||||||
|
{owner?.username && (
|
||||||
|
<Link
|
||||||
|
href={`/u/${owner.username}`}
|
||||||
|
className="text-xs text-neutral-500 hover:text-neutral-700 transition-colors"
|
||||||
|
>
|
||||||
|
@{owner.username}
|
||||||
|
</Link>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
{owner?.username && (
|
|
||||||
<Link
|
|
||||||
href={`/u/${owner.username}`}
|
|
||||||
className="text-xs text-neutral-500 hover:text-neutral-700 transition-colors"
|
|
||||||
>
|
|
||||||
@{owner.username}
|
|
||||||
</Link>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Breadcrumb — only shown when navigated into a building */}
|
{/* Breadcrumb — only shown when navigated into a building */}
|
||||||
{building && (
|
{building && (
|
||||||
<div className="border-t border-neutral-100 px-3 py-1.5">
|
<div className="border-t border-neutral-100 px-3 py-1.5">
|
||||||
<div className="flex items-center gap-1 text-xs">
|
<div className="flex items-center gap-1 text-xs">
|
||||||
<button
|
|
||||||
onClick={() => handleBreadcrumbClick('root')}
|
|
||||||
className="text-neutral-500 hover:text-neutral-800 transition-colors"
|
|
||||||
>
|
|
||||||
Site
|
|
||||||
</button>
|
|
||||||
|
|
||||||
{building && (
|
|
||||||
<>
|
|
||||||
<ChevronRight className="w-3 h-3 text-neutral-400" />
|
|
||||||
<button
|
<button
|
||||||
onClick={() => handleBreadcrumbClick('building')}
|
onClick={() => handleBreadcrumbClick('root')}
|
||||||
className={`transition-colors truncate ${level ? 'text-neutral-500 hover:text-neutral-800' : 'text-neutral-800 font-medium'}`}
|
className="text-neutral-500 hover:text-neutral-800 transition-colors"
|
||||||
>
|
>
|
||||||
{building.name || 'Building'}
|
Site
|
||||||
</button>
|
</button>
|
||||||
</>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{level && (
|
{building && (
|
||||||
<>
|
<>
|
||||||
<ChevronRight className="w-3 h-3 text-neutral-400" />
|
<ChevronRight className="w-3 h-3 text-neutral-400" />
|
||||||
<button
|
<button
|
||||||
onClick={() => handleBreadcrumbClick('level')}
|
onClick={() => handleBreadcrumbClick('building')}
|
||||||
className={`transition-colors truncate ${zone ? 'text-neutral-500 hover:text-neutral-800' : 'text-neutral-800 font-medium'}`}
|
className={`transition-colors truncate ${level ? 'text-neutral-500 hover:text-neutral-800' : 'text-neutral-800 font-medium'}`}
|
||||||
>
|
>
|
||||||
{level.name || `Level ${level.level}`}
|
{building.name || 'Building'}
|
||||||
</button>
|
</button>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{zone && (
|
{level && (
|
||||||
<>
|
<>
|
||||||
<ChevronRight className="w-3 h-3 text-neutral-400" />
|
<ChevronRight className="w-3 h-3 text-neutral-400" />
|
||||||
<span className={`transition-colors truncate ${selectedNode ? 'text-neutral-500' : 'text-neutral-800 font-medium'}`}>
|
<button
|
||||||
{zone.name}
|
onClick={() => handleBreadcrumbClick('level')}
|
||||||
</span>
|
className={`transition-colors truncate ${zone ? 'text-neutral-500 hover:text-neutral-800' : 'text-neutral-800 font-medium'}`}
|
||||||
</>
|
>
|
||||||
)}
|
{level.name || `Level ${level.level}`}
|
||||||
|
</button>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
{selectedNode && zone && (
|
{zone && (
|
||||||
<>
|
<>
|
||||||
<ChevronRight className="w-3 h-3 text-neutral-400" />
|
<ChevronRight className="w-3 h-3 text-neutral-400" />
|
||||||
<span className="text-neutral-800 font-medium truncate">{getNodeName(selectedNode)}</span>
|
<span
|
||||||
</>
|
className={`transition-colors truncate ${selectedNode ? 'text-neutral-500' : 'text-neutral-800 font-medium'}`}
|
||||||
)}
|
>
|
||||||
</div>
|
{zone.name}
|
||||||
</div>
|
</span>
|
||||||
)}
|
</>
|
||||||
</div>
|
)}
|
||||||
|
|
||||||
{/* Level List (only when building is selected) */}
|
{selectedNode && zone && (
|
||||||
{building && levels.length > 0 && (
|
<>
|
||||||
<div className="flex flex-col gap-1 bg-white/80 backdrop-blur-sm rounded-lg p-2 shadow-[0_1px_4px_rgba(0,0,0,0.06),0_0_0_1px_rgba(0,0,0,0.03)] w-40">
|
<ChevronRight className="w-3 h-3 text-neutral-400" />
|
||||||
<span className="text-xs text-neutral-500 px-2 pb-1">Levels</span>
|
<span className="text-neutral-800 font-medium truncate">
|
||||||
{levels.map((lvl) => (
|
{getNodeName(selectedNode)}
|
||||||
<button
|
</span>
|
||||||
key={lvl.id}
|
</>
|
||||||
onClick={() => handleLevelClick(lvl.id)}
|
)}
|
||||||
className={`text-left px-2 py-1 rounded text-sm transition-colors ${
|
</div>
|
||||||
lvl.id === selection.levelId
|
</div>
|
||||||
? 'bg-blue-500 text-white'
|
|
||||||
: 'text-neutral-700 hover:bg-neutral-100'
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
{lvl.name || `Level ${lvl.level}`}
|
|
||||||
</button>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Controls Panel - Top Right */}
|
|
||||||
<div className="absolute top-4 right-4 z-10 flex flex-col gap-2">
|
|
||||||
{/* Visibility Controls */}
|
|
||||||
{(canShowScans || canShowGuides) && (
|
|
||||||
<div className="flex flex-col gap-1 bg-white/80 backdrop-blur-sm rounded-lg p-2 shadow-[0_1px_4px_rgba(0,0,0,0.06),0_0_0_1px_rgba(0,0,0,0.03)]">
|
|
||||||
<span className="text-xs text-neutral-500 px-2 pb-1">Visibility</span>
|
|
||||||
{canShowScans && (
|
|
||||||
<button
|
|
||||||
onClick={() => useViewer.getState().setShowScans(!showScans)}
|
|
||||||
className={`flex items-center gap-2 px-2 py-1 rounded text-sm transition-colors ${
|
|
||||||
showScans ? 'bg-blue-500 text-white' : 'text-neutral-700 hover:bg-neutral-100'
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
<Box className="w-4 h-4" />
|
|
||||||
3D Scans
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
{canShowGuides && (
|
|
||||||
<button
|
|
||||||
onClick={() => useViewer.getState().setShowGuides(!showGuides)}
|
|
||||||
className={`flex items-center gap-2 px-2 py-1 rounded text-sm transition-colors ${
|
|
||||||
showGuides ? 'bg-blue-500 text-white' : 'text-neutral-700 hover:bg-neutral-100'
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
<Image className="w-4 h-4" />
|
|
||||||
Guides
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Camera Mode */}
|
|
||||||
<div className="flex flex-col gap-1 bg-white/80 backdrop-blur-sm rounded-lg p-2 shadow-[0_1px_4px_rgba(0,0,0,0.06),0_0_0_1px_rgba(0,0,0,0.03)]">
|
|
||||||
<span className="text-xs text-neutral-500 px-2 pb-1">Camera</span>
|
|
||||||
<button
|
|
||||||
onClick={() => useViewer.getState().setCameraMode(cameraMode === 'perspective' ? 'orthographic' : 'perspective')}
|
|
||||||
className="flex items-center gap-2 px-2 py-1 rounded text-sm text-neutral-700 hover:bg-neutral-100 transition-colors"
|
|
||||||
>
|
|
||||||
{cameraMode === 'perspective' ? (
|
|
||||||
<Eye className="w-4 h-4" />
|
|
||||||
) : (
|
|
||||||
<EyeOff className="w-4 h-4" />
|
|
||||||
)}
|
)}
|
||||||
{cameraMode === 'perspective' ? 'Perspective' : 'Orthographic'}
|
</div>
|
||||||
</button>
|
|
||||||
|
{/* Level List (only when building is selected) */}
|
||||||
|
{building && levels.length > 0 && (
|
||||||
|
<div className="flex flex-col gap-1 bg-white/80 backdrop-blur-sm rounded-lg p-2 shadow-[0_1px_4px_rgba(0,0,0,0.06),0_0_0_1px_rgba(0,0,0,0.03)] w-40">
|
||||||
|
<span className="text-xs text-neutral-500 px-2 pb-1">Levels</span>
|
||||||
|
{levels.map((lvl) => (
|
||||||
|
<button
|
||||||
|
key={lvl.id}
|
||||||
|
onClick={() => handleLevelClick(lvl.id)}
|
||||||
|
className={`text-left px-2 py-1 rounded text-sm transition-colors ${
|
||||||
|
lvl.id === selection.levelId
|
||||||
|
? 'bg-blue-500 text-white'
|
||||||
|
: 'text-neutral-700 hover:bg-neutral-100'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{lvl.name || `Level ${lvl.level}`}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Level Mode */}
|
{/* Controls Panel - Top Right */}
|
||||||
<div className="flex flex-col gap-1 bg-white/80 backdrop-blur-sm rounded-lg p-2 shadow-[0_1px_4px_rgba(0,0,0,0.06),0_0_0_1px_rgba(0,0,0,0.03)]">
|
<div className="absolute top-4 right-4 z-20 flex flex-col gap-2">
|
||||||
<span className="text-xs text-neutral-500 px-2 pb-1">Level Mode</span>
|
{/* Visibility Controls */}
|
||||||
<button
|
{(canShowScans || canShowGuides) && (
|
||||||
onClick={() => useViewer.getState().setLevelMode('stacked')}
|
<div className="flex flex-col gap-1 bg-white/80 backdrop-blur-sm rounded-lg p-2 shadow-[0_1px_4px_rgba(0,0,0,0.06),0_0_0_1px_rgba(0,0,0,0.03)]">
|
||||||
className={`flex items-center gap-2 px-2 py-1 rounded text-sm transition-colors ${
|
<span className="text-xs text-neutral-500 px-2 pb-1">Visibility</span>
|
||||||
levelMode === 'stacked' ? 'bg-blue-500 text-white' : 'text-neutral-700 hover:bg-neutral-100'
|
{canShowScans && (
|
||||||
}`}
|
<button
|
||||||
>
|
onClick={() => useViewer.getState().setShowScans(!showScans)}
|
||||||
<Layers className="w-4 h-4" />
|
className={`flex items-center gap-2 px-2 py-1 rounded text-sm transition-colors ${
|
||||||
Stacked
|
showScans ? 'bg-blue-500 text-white' : 'text-neutral-700 hover:bg-neutral-100'
|
||||||
</button>
|
}`}
|
||||||
<button
|
>
|
||||||
onClick={() => useViewer.getState().setLevelMode('exploded')}
|
<Box className="w-4 h-4" />
|
||||||
className={`flex items-center gap-2 px-2 py-1 rounded text-sm transition-colors ${
|
3D Scans
|
||||||
levelMode === 'exploded' ? 'bg-blue-500 text-white' : 'text-neutral-700 hover:bg-neutral-100'
|
</button>
|
||||||
}`}
|
)}
|
||||||
>
|
{canShowGuides && (
|
||||||
<Layers2 className="w-4 h-4" />
|
<button
|
||||||
Exploded
|
onClick={() => useViewer.getState().setShowGuides(!showGuides)}
|
||||||
</button>
|
className={`flex items-center gap-2 px-2 py-1 rounded text-sm transition-colors ${
|
||||||
<button
|
showGuides ? 'bg-blue-500 text-white' : 'text-neutral-700 hover:bg-neutral-100'
|
||||||
onClick={() => useViewer.getState().setLevelMode('solo')}
|
}`}
|
||||||
className={`flex items-center gap-2 px-2 py-1 rounded text-sm transition-colors ${
|
>
|
||||||
levelMode === 'solo' ? 'bg-blue-500 text-white' : 'text-neutral-700 hover:bg-neutral-100'
|
<Image className="w-4 h-4" />
|
||||||
}`}
|
Guides
|
||||||
>
|
</button>
|
||||||
<Diamond className="w-4 h-4" />
|
)}
|
||||||
Solo
|
</div>
|
||||||
</button>
|
)}
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Wall Mode */}
|
{/* Camera Mode */}
|
||||||
<div className="flex flex-col gap-1 bg-white/80 backdrop-blur-sm rounded-lg p-2 shadow-[0_1px_4px_rgba(0,0,0,0.06),0_0_0_1px_rgba(0,0,0,0.03)]">
|
<div className="flex flex-col gap-1 bg-white/80 backdrop-blur-sm rounded-lg p-2 shadow-[0_1px_4px_rgba(0,0,0,0.06),0_0_0_1px_rgba(0,0,0,0.03)]">
|
||||||
<span className="text-xs text-neutral-500 px-2 pb-1">Wall Mode</span>
|
<span className="text-xs text-neutral-500 px-2 pb-1">Camera</span>
|
||||||
<button
|
<button
|
||||||
onClick={() => useViewer.getState().setWallMode('cutaway')}
|
onClick={() =>
|
||||||
className={`flex items-center gap-2 px-2 py-1 rounded text-sm transition-colors ${
|
useViewer
|
||||||
wallMode === 'cutaway' ? 'bg-blue-500 text-white' : 'text-neutral-700 hover:bg-neutral-100'
|
.getState()
|
||||||
}`}
|
.setCameraMode(cameraMode === 'perspective' ? 'orthographic' : 'perspective')
|
||||||
>
|
}
|
||||||
<img alt="Cutaway" height={16} src="/icons/wallcut.png" width={16} className="w-4 h-4" />
|
className="flex items-center gap-2 px-2 py-1 rounded text-sm text-neutral-700 hover:bg-neutral-100 transition-colors"
|
||||||
Cutaway
|
>
|
||||||
</button>
|
{cameraMode === 'perspective' ? (
|
||||||
<button
|
<Eye className="w-4 h-4" />
|
||||||
onClick={() => useViewer.getState().setWallMode('up')}
|
) : (
|
||||||
className={`flex items-center gap-2 px-2 py-1 rounded text-sm transition-colors ${
|
<EyeOff className="w-4 h-4" />
|
||||||
wallMode === 'up' ? 'bg-blue-500 text-white' : 'text-neutral-700 hover:bg-neutral-100'
|
)}
|
||||||
}`}
|
{cameraMode === 'perspective' ? 'Perspective' : 'Orthographic'}
|
||||||
>
|
</button>
|
||||||
<img alt="Full Height" height={16} src="/icons/room.png" width={16} className="w-4 h-4" />
|
</div>
|
||||||
Full Height
|
|
||||||
</button>
|
{/* Level Mode */}
|
||||||
<button
|
<div className="flex flex-col gap-1 bg-white/80 backdrop-blur-sm rounded-lg p-2 shadow-[0_1px_4px_rgba(0,0,0,0.06),0_0_0_1px_rgba(0,0,0,0.03)]">
|
||||||
onClick={() => useViewer.getState().setWallMode('down')}
|
<span className="text-xs text-neutral-500 px-2 pb-1">Level Mode</span>
|
||||||
className={`flex items-center gap-2 px-2 py-1 rounded text-sm transition-colors ${
|
<button
|
||||||
wallMode === 'down' ? 'bg-blue-500 text-white' : 'text-neutral-700 hover:bg-neutral-100'
|
onClick={() => useViewer.getState().setLevelMode('stacked')}
|
||||||
}`}
|
className={`flex items-center gap-2 px-2 py-1 rounded text-sm transition-colors ${
|
||||||
>
|
levelMode === 'stacked'
|
||||||
<img alt="Low" height={16} src="/icons/walllow.png" width={16} className="w-4 h-4" />
|
? 'bg-blue-500 text-white'
|
||||||
Low
|
: 'text-neutral-700 hover:bg-neutral-100'
|
||||||
</button>
|
}`}
|
||||||
|
>
|
||||||
|
<Layers className="w-4 h-4" />
|
||||||
|
Stacked
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => useViewer.getState().setLevelMode('exploded')}
|
||||||
|
className={`flex items-center gap-2 px-2 py-1 rounded text-sm transition-colors ${
|
||||||
|
levelMode === 'exploded'
|
||||||
|
? 'bg-blue-500 text-white'
|
||||||
|
: 'text-neutral-700 hover:bg-neutral-100'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<Layers2 className="w-4 h-4" />
|
||||||
|
Exploded
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => useViewer.getState().setLevelMode('solo')}
|
||||||
|
className={`flex items-center gap-2 px-2 py-1 rounded text-sm transition-colors ${
|
||||||
|
levelMode === 'solo'
|
||||||
|
? 'bg-blue-500 text-white'
|
||||||
|
: 'text-neutral-700 hover:bg-neutral-100'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<Diamond className="w-4 h-4" />
|
||||||
|
Solo
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Wall Mode */}
|
||||||
|
<div className="flex flex-col gap-1 bg-white/80 backdrop-blur-sm rounded-lg p-2 shadow-[0_1px_4px_rgba(0,0,0,0.06),0_0_0_1px_rgba(0,0,0,0.03)]">
|
||||||
|
<span className="text-xs text-neutral-500 px-2 pb-1">Wall Mode</span>
|
||||||
|
<button
|
||||||
|
onClick={() => useViewer.getState().setWallMode('cutaway')}
|
||||||
|
className={`flex items-center gap-2 px-2 py-1 rounded text-sm transition-colors ${
|
||||||
|
wallMode === 'cutaway'
|
||||||
|
? 'bg-blue-500 text-white'
|
||||||
|
: 'text-neutral-700 hover:bg-neutral-100'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
alt="Cutaway"
|
||||||
|
height={16}
|
||||||
|
src="/icons/wallcut.png"
|
||||||
|
width={16}
|
||||||
|
className="w-4 h-4"
|
||||||
|
/>
|
||||||
|
Cutaway
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => useViewer.getState().setWallMode('up')}
|
||||||
|
className={`flex items-center gap-2 px-2 py-1 rounded text-sm transition-colors ${
|
||||||
|
wallMode === 'up' ? 'bg-blue-500 text-white' : 'text-neutral-700 hover:bg-neutral-100'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
alt="Full Height"
|
||||||
|
height={16}
|
||||||
|
src="/icons/room.png"
|
||||||
|
width={16}
|
||||||
|
className="w-4 h-4"
|
||||||
|
/>
|
||||||
|
Full Height
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => useViewer.getState().setWallMode('down')}
|
||||||
|
className={`flex items-center gap-2 px-2 py-1 rounded text-sm transition-colors ${
|
||||||
|
wallMode === 'down'
|
||||||
|
? 'bg-blue-500 text-white'
|
||||||
|
: 'text-neutral-700 hover:bg-neutral-100'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<img alt="Low" height={16} src="/icons/walllow.png" width={16} className="w-4 h-4" />
|
||||||
|
Low
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user