Files
Mujoco_WASM/web_platform/src/app/components/RightSidebarTabs.tsx
T
chenlin fa5485049a
web-platform-release / Build and publish release (push) Has been cancelled
web-platform-ci / TypeScript, lint, unit, build (push) Has been cancelled
web-platform-ci / Playwright E2E (push) Has been cancelled
web-platform-ci / TypeScript, lint, unit, build (pull_request) Has been cancelled
web-platform-ci / Playwright E2E (pull_request) Has been cancelled
feat(web-platform): release V0.8.3 常规界面优化
2026-09-04 15:55:36 +08:00

66 lines
2.3 KiB
TypeScript

import { Database, PanelRight, SlidersHorizontal, type LucideIcon } from 'lucide-react';
export type RightSidebarView = 'inspector' | 'controls' | 'data';
const VIEWS: ReadonlyArray<{
value: RightSidebarView;
label: string;
icon: LucideIcon;
}> = [
{ value: 'inspector', label: '检查器', icon: PanelRight },
{ value: 'controls', label: '控制台', icon: SlidersHorizontal },
{ value: 'data', label: '数据录制', icon: Database },
];
export function RightSidebarTabs({
value,
onChange,
}: {
value: RightSidebarView;
onChange: (value: RightSidebarView) => void;
}) {
return (
<div
role="tablist"
aria-label="右侧工作区视图"
className="grid shrink-0 grid-cols-3 gap-1 border-b border-border bg-panel-muted/40 p-2"
>
{VIEWS.map((view, index) => {
const Icon = view.icon;
const selected = value === view.value;
return (
<button
key={view.value}
type="button"
role="tab"
aria-selected={selected}
tabIndex={selected ? 0 : -1}
className={`flex min-w-0 items-center justify-center gap-1.5 rounded-md px-1 py-2 text-[11px] font-medium transition-colors ${
selected
? 'bg-panel text-accent shadow-sm'
: 'text-text-tertiary hover:bg-element-hover hover:text-text-primary'
}`}
onClick={() => onChange(view.value)}
onKeyDown={(event) => {
let nextIndex: number | undefined;
if (event.key === 'ArrowLeft') nextIndex = (index + VIEWS.length - 1) % VIEWS.length;
else if (event.key === 'ArrowRight') nextIndex = (index + 1) % VIEWS.length;
else if (event.key === 'Home') nextIndex = 0;
else if (event.key === 'End') nextIndex = VIEWS.length - 1;
if (nextIndex === undefined) return;
event.preventDefault();
onChange(VIEWS[nextIndex].value);
const tabs =
event.currentTarget.parentElement?.querySelectorAll<HTMLElement>('[role="tab"]');
tabs?.[nextIndex]?.focus();
}}
>
<Icon className="h-3.5 w-3.5 shrink-0" aria-hidden="true" />
<span className="truncate">{view.label}</span>
</button>
);
})}
</div>
);
}