"use client";
import MenuItem from "@mui/material/MenuItem";
import Select from "@mui/material/Select";
import clsx from "clsx";
import { useId, useState, type ReactNode } from "react";
import {
FiBox,
FiCamera,
FiCheck,
FiChevronDown,
FiChevronRight,
FiClock,
FiCrosshair,
FiDroplet,
FiHome,
FiInfo,
FiLayers,
FiMaximize,
FiPlus,
FiRotateCcw,
FiSliders,
FiTrash2,
FiX,
} from "react-icons/fi";
import type {
SceneAssetSelection,
SceneCommand,
SceneMode,
SceneRuntimeState,
} from "./sceneProtocol";
const sceneModes: Array<{ mode: SceneMode; label: string }> = [
{ mode: "network", label: "供水管网" },
{ mode: "hydraulic", label: "泵组与管网" },
{ mode: "map", label: "站区轻量版" },
{ mode: "detail", label: "站房精细版" },
{ mode: "pump", label: "CAD 泵房参考" },
{ mode: "meters", label: "设备样件" },
];
const glassSurface =
"bg-[linear-gradient(135deg,rgba(255,255,255,0.50),rgba(224,239,250,0.28))] [backdrop-filter:blur(24px)_saturate(170%)_contrast(94%)] [-webkit-backdrop-filter:blur(24px)_saturate(170%)_contrast(94%)] [box-shadow:inset_0_1px_0_rgba(255,255,255,0.88),inset_0_-1px_0_rgba(112,145,168,0.18),0_18px_50px_rgba(15,43,69,0.20)] ring-1 ring-white/55";
const fieldClass =
"h-10 w-full rounded-lg border border-slate-300/70 bg-white/55 px-3 text-sm text-slate-800 outline-none transition focus:border-blue-500 focus:bg-white/80 focus:ring-2 focus:ring-blue-500/20 disabled:cursor-not-allowed disabled:opacity-50";
const selectSx = {
height: 40,
borderRadius: "0.5rem",
backgroundColor: "rgba(255,255,255,0.55)",
color: "#1e293b",
fontSize: "0.875rem",
transition: "background-color 150ms ease, box-shadow 150ms ease",
"&:hover": {
backgroundColor: "rgba(255,255,255,0.78)",
},
"& .MuiSelect-select": {
display: "flex",
alignItems: "center",
minHeight: "0 !important",
padding: "8px 38px 8px 12px !important",
},
"& .MuiSelect-icon": {
right: 12,
color: "#64748b",
fontSize: 16,
transition: "transform 150ms ease",
},
"& .MuiOutlinedInput-notchedOutline": {
borderColor: "rgba(148,163,184,0.7)",
},
"&:hover .MuiOutlinedInput-notchedOutline": {
borderColor: "rgba(59,130,246,0.65)",
},
"&.Mui-focused": {
backgroundColor: "rgba(255,255,255,0.82)",
boxShadow: "0 0 0 3px rgba(59,130,246,0.16)",
},
"&.Mui-focused .MuiOutlinedInput-notchedOutline": {
borderColor: "#3b82f6",
borderWidth: 1,
},
};
const selectMenuProps = {
disableScrollLock: true,
PaperProps: {
elevation: 0,
sx: {
mt: 0.75,
overflow: "hidden",
borderRadius: "0.75rem",
border: "1px solid rgba(255,255,255,0.72)",
background:
"linear-gradient(135deg, rgba(244,249,252,0.94), rgba(225,239,248,0.88))",
backdropFilter: "blur(24px) saturate(155%)",
WebkitBackdropFilter: "blur(24px) saturate(155%)",
boxShadow:
"inset 0 1px 0 rgba(255,255,255,0.92), 0 16px 42px rgba(15,43,69,0.22)",
},
},
MenuListProps: {
sx: {
p: 0.75,
},
},
};
const selectMenuItemSx = {
minHeight: 40,
gap: 1,
borderRadius: "0.5rem",
px: 1.25,
color: "#334155",
fontSize: "0.875rem",
transition: "background-color 120ms ease, color 120ms ease",
"&:hover": {
backgroundColor: "rgba(255,255,255,0.68)",
color: "#1d4ed8",
},
"&.Mui-selected": {
backgroundColor: "#2563eb",
color: "#fff",
},
"&.Mui-selected:hover": {
backgroundColor: "#1d4ed8",
},
"&.Mui-focusVisible": {
outline: "2px solid rgba(59,130,246,0.42)",
outlineOffset: -2,
},
};
export type ControlTab = "scene" | "style" | "properties";
export type ThreeDimensionalControlsProps = {
open: boolean;
activeTab: ControlTab;
ready: boolean;
state: SceneRuntimeState;
selection: SceneAssetSelection | null;
timelineOpen: boolean;
onOpenChange: (open: boolean) => void;
onTimelineOpenChange: (open: boolean) => void;
onTabChange: (tab: ControlTab) => void;
onCommand: (command: SceneCommand) => void;
};
export function ThreeDimensionalControls({
open,
activeTab,
ready,
state,
selection,
timelineOpen,
onOpenChange,
onTimelineOpenChange,
onTabChange,
onCommand,
}: ThreeDimensionalControlsProps) {
const [cameraLabel, setCameraLabel] = useState("");
const selectTab = (tab: ControlTab) => {
if (open && activeTab === tab) {
onOpenChange(false);
return;
}
onTabChange(tab);
onOpenChange(true);
};
return (
<>
{open && (
)}
>
);
}
function ToolButton({ label, active = false, disabled = false, onClick, children }: { label: string; active?: boolean; disabled?: boolean; onClick: () => void; children: ReactNode }) {
return (
{label}
);
}
function IconButton({ label, disabled = false, onClick, children }: { label: string; disabled?: boolean; onClick: () => void; children: ReactNode }) {
return (
);
}
function TabButton({ active, onClick, icon, children }: { active: boolean; onClick: () => void; icon: ReactNode; children: ReactNode }) {
return (
);
}
function ControlSection({ title, description, children }: { title: string; description?: string; children: ReactNode }) {
return (
{title}
{description &&
{description}
}
{children}
);
}
function SelectField({ label, value, options, onChange }: { label: string; value: string; options: Array<{ value: string; label: string }>; onChange: (value: string) => void }) {
const labelId = useId();
const selected = options.find((option) => option.value === value);
return (
{label}
);
}
function SwitchRow({ label, checked, onChange }: { label: string; checked: boolean; onChange: (checked: boolean) => void }) {
return (
);
}
function LabeledSlider({ label, value, min, max, step, disabled = false, onChange }: { label: string; value: number; min: number; max: number; step: number; disabled?: boolean; onChange: (value: number) => void }) {
return (
);
}
function ColorInput({ label, value, onChange }: { label: string; value: string; onChange: (value: string) => void }) {
return (
);
}
function NumberInput({ label, value, onCommit }: { label: string; value: number; onCommit: (value: number) => void }) {
return (
);
}
function AssetProperties({ selection, onCommand }: { selection: SceneAssetSelection | null; onCommand: (command: SceneCommand) => void }) {
if (!selection) {
return (
尚未选择三维构件
在模型中点击节点、管段、泵或阀门,这里会显示模型属性和当前后端结果。
);
}
return (
{selection.title}
模型编号 {selection.elementId}
{selection.sections.map((section) => (
{section.title}
{section.fields.map((field, index) => (
0 && "border-t border-slate-200/55")}>
- {field.label}
- {field.value}
))}
))}
{selection.neighbors.length > 0 && (
相邻管段
{selection.neighbors.map((neighbor) => (
))}
)}
);
}