Enhance ViewportPanel with additional controls and functionality

This commit is contained in:
2026-04-11 14:14:26 +02:00
parent 6450a03f5c
commit f012f01708

View File

@@ -1,9 +1,30 @@
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
import { ViewportCanvas } from "./ViewportCanvas";
import { getViewportDisplayModeLabel, getViewportPanelLabel } from "./viewport-layout";
import {
VIEWPORT_LAYOUT_MODES,
getViewportDisplayModeLabel,
getViewportLayoutModeLabel,
getViewportPanelLabel
} from "./viewport-layout";
import { VIEWPORT_VIEW_MODES, getViewportViewModeLabel } from "./viewport-view-modes";
export function ViewportPanel({ panelId, panelState, layoutMode, isActive, className, style, world, sceneDocument, projectAssets, loadedModelAssets, loadedImageAssets, whiteboxSelectionMode, whiteboxSnapEnabled, whiteboxSnapStep, selection, toolMode, toolPreview, transformSession, cameraState, focusRequestId, focusSelection, onActivatePanel, onSetPanelViewMode, onSetPanelDisplayMode, onCommitCreation, onCameraStateChange, onToolPreviewChange, onTransformSessionChange, onTransformCommit, onTransformCancel, onSelectionChange }) {
import {
WHITEBOX_SELECTION_MODES,
getWhiteboxSelectionModeLabel
} from "../core/whitebox-selection-mode";
const VIEWPORT_DISPLAY_MODES = ["normal", "authoring", "wireframe"];
function getPanelScopedTestId(panelId, name) {
return `viewport-panel-${panelId}-${name}`;
}
function getSharedControlTestId(panelId, isActive, sharedId, fallbackId = sharedId) {
return isActive ? sharedId : getPanelScopedTestId(panelId, fallbackId);
}
export function ViewportPanel({ panelId, panelState, layoutMode, isActive, className, style, world, sceneDocument, projectAssets, loadedModelAssets, loadedImageAssets, whiteboxSelectionMode, whiteboxSnapEnabled, whiteboxSnapStepDraft, whiteboxSnapStep, viewportGridVisible, selection, toolMode, toolPreview, transformSession, canTranslateSelectedTarget, canRotateSelectedTarget, canScaleSelectedTarget, cameraState, focusRequestId, focusSelection, isAddMenuOpen, onActivatePanel, onOpenAddMenu, onSetViewportLayoutMode, onSetPanelViewMode, onSetPanelDisplayMode, onCommitCreation, onCameraStateChange, onToolPreviewChange, onBeginTransformOperation, onWhiteboxSelectionModeChange, onViewportGridToggle, onWhiteboxSnapToggle, onWhiteboxSnapStepDraftChange, onWhiteboxSnapStepBlur, onTransformSessionChange, onTransformCommit, onTransformCancel, onSelectionChange }) {
const shouldShow = layoutMode === "quad" || isActive;
const panelStyle = shouldShow ? style : { ...(style ?? {}), display: "none" };
return (_jsxs("section", { className: `viewport-panel ${layoutMode === "single" ? "viewport-panel--single" : "viewport-panel--quad"} ${className ?? ""}`.trim(), "data-testid": `viewport-panel-${panelId}`, "data-active": isActive ? "true" : "false", "data-viewport-panel-id": panelId, "aria-hidden": shouldShow ? undefined : true, "aria-label": `${getViewportPanelLabel(panelId)} viewport panel`, style: panelStyle, onPointerDownCapture: () => onActivatePanel(panelId), onFocusCapture: () => onActivatePanel(panelId), children: [_jsxs("div", { className: "viewport-panel__header", children: [layoutMode !== "quad" ? null : (_jsx("div", { className: "viewport-panel__meta", children: _jsxs("div", { className: "viewport-panel__title-row", children: [_jsx("div", { className: "viewport-panel__title", children: getViewportPanelLabel(panelId) }), !isActive ? null : (_jsx("div", { className: "viewport-panel__active-badge", "data-testid": `viewport-panel-active-badge-${panelId}`, children: "Active" }))] }) })), _jsxs("div", { className: "viewport-panel__controls", children: [_jsx("div", { className: "viewport-panel__control-group", role: "group", "aria-label": `${getViewportPanelLabel(panelId)} view mode`, children: VIEWPORT_VIEW_MODES.map((viewMode) => (_jsx("button", { className: `viewport-panel__button ${panelState.viewMode === viewMode ? "viewport-panel__button--active" : ""}`, type: "button", "data-testid": `viewport-panel-${panelId}-view-${viewMode}`, "aria-pressed": panelState.viewMode === viewMode, onClick: () => onSetPanelViewMode(panelId, viewMode), children: getViewportViewModeLabel(viewMode) }, viewMode))) }), _jsx("div", { className: "viewport-panel__control-group", role: "group", "aria-label": `${getViewportPanelLabel(panelId)} display mode`, children: ["normal", "authoring", "wireframe"].map((displayMode) => (_jsx("button", { className: `viewport-panel__button ${panelState.displayMode === displayMode ? "viewport-panel__button--active" : ""}`, type: "button", "data-testid": `viewport-panel-${panelId}-display-${displayMode}`, "aria-pressed": panelState.displayMode === displayMode, onClick: () => onSetPanelDisplayMode(panelId, displayMode), children: getViewportDisplayModeLabel(displayMode) }, displayMode))) })] })] }), _jsx(ViewportCanvas, { panelId: panelId, world: world, sceneDocument: sceneDocument, projectAssets: projectAssets, loadedModelAssets: loadedModelAssets, loadedImageAssets: loadedImageAssets, whiteboxSelectionMode: whiteboxSelectionMode, whiteboxSnapEnabled: whiteboxSnapEnabled, whiteboxSnapStep: whiteboxSnapStep, selection: selection, toolMode: toolMode, toolPreview: toolPreview, transformSession: transformSession, cameraState: cameraState, viewMode: panelState.viewMode, displayMode: panelState.displayMode, layoutMode: layoutMode, isActivePanel: isActive, focusRequestId: focusRequestId, focusSelection: focusSelection, onSelectionChange: onSelectionChange, onCommitCreation: onCommitCreation, onCameraStateChange: onCameraStateChange, onToolPreviewChange: onToolPreviewChange, onTransformSessionChange: onTransformSessionChange, onTransformCommit: onTransformCommit, onTransformCancel: onTransformCancel })] }));
const transformButtonsDisabled = toolMode !== "select";
return (_jsxs("section", { className: `viewport-panel ${layoutMode === "single" ? "viewport-panel--single" : "viewport-panel--quad"} ${className ?? ""}`.trim(), "data-testid": `viewport-panel-${panelId}`, "data-active": isActive ? "true" : "false", "data-viewport-panel-id": panelId, "aria-hidden": shouldShow ? undefined : true, "aria-label": `${getViewportPanelLabel(panelId)} viewport panel`, style: panelStyle, onPointerDownCapture: () => onActivatePanel(panelId), onFocusCapture: () => onActivatePanel(panelId), children: [_jsx(ViewportCanvas, { panelId: panelId, world: world, sceneDocument: sceneDocument, projectAssets: projectAssets, loadedModelAssets: loadedModelAssets, loadedImageAssets: loadedImageAssets, whiteboxSelectionMode: whiteboxSelectionMode, whiteboxSnapEnabled: whiteboxSnapEnabled, whiteboxSnapStep: whiteboxSnapStep, viewportGridVisible: viewportGridVisible, selection: selection, toolMode: toolMode, toolPreview: toolPreview, transformSession: transformSession, cameraState: cameraState, viewMode: panelState.viewMode, displayMode: panelState.displayMode, layoutMode: layoutMode, isActivePanel: isActive, focusRequestId: focusRequestId, focusSelection: focusSelection, onSelectionChange: onSelectionChange, onCommitCreation: onCommitCreation, onCameraStateChange: onCameraStateChange, onToolPreviewChange: onToolPreviewChange, onTransformSessionChange: onTransformSessionChange, onTransformCommit: onTransformCommit, onTransformCancel: onTransformCancel }), _jsx("div", { className: "viewport-panel__overlay viewport-panel__overlay--top", children: _jsxs("div", { className: "viewport-panel__overlay-scroll", children: [_jsx("button", { className: "viewport-panel__button viewport-panel__button--accent", type: "button", "data-testid": getSharedControlTestId(panelId, isActive, "outliner-add-button", "add-button"), "aria-haspopup": "menu", "aria-expanded": isAddMenuOpen, onClick: onOpenAddMenu, children: "Add" }), _jsx("div", { className: "viewport-panel__control-group", role: "group", "aria-label": "Viewport layout mode", children: VIEWPORT_LAYOUT_MODES.map((mode) => (_jsx("button", { className: `viewport-panel__button ${layoutMode === mode ? "viewport-panel__button--active" : ""}`, type: "button", "data-testid": getSharedControlTestId(panelId, isActive, `viewport-layout-${mode}`, `layout-${mode}`), "aria-pressed": layoutMode === mode, onClick: () => onSetViewportLayoutMode(mode), children: getViewportLayoutModeLabel(mode) }, mode))) }), _jsx("div", { className: "viewport-panel__control-group", role: "group", "aria-label": `${getViewportPanelLabel(panelId)} view mode`, children: VIEWPORT_VIEW_MODES.map((viewMode) => (_jsx("button", { className: `viewport-panel__button ${panelState.viewMode === viewMode ? "viewport-panel__button--active" : ""}`, type: "button", "data-testid": `viewport-panel-${panelId}-view-${viewMode}`, "aria-pressed": panelState.viewMode === viewMode, onClick: () => onSetPanelViewMode(panelId, viewMode), children: getViewportViewModeLabel(viewMode) }, viewMode))) }), _jsx("div", { className: "viewport-panel__control-group", role: "group", "aria-label": `${getViewportPanelLabel(panelId)} display mode`, children: VIEWPORT_DISPLAY_MODES.map((displayMode) => (_jsx("button", { className: `viewport-panel__button ${panelState.displayMode === displayMode ? "viewport-panel__button--active" : ""}`, type: "button", "data-testid": `viewport-panel-${panelId}-display-${displayMode}`, "aria-pressed": panelState.displayMode === displayMode, onClick: () => onSetPanelDisplayMode(panelId, displayMode), children: getViewportDisplayModeLabel(displayMode) }, displayMode))) }), _jsxs("div", { className: "viewport-panel__control-group", role: "group", "aria-label": "Whitebox snap settings", children: [_jsx("button", { className: `viewport-panel__button ${viewportGridVisible ? "viewport-panel__button--active" : ""}`, type: "button", "data-testid": getSharedControlTestId(panelId, isActive, "viewport-grid-toggle"), "aria-pressed": viewportGridVisible, onClick: onViewportGridToggle, children: viewportGridVisible ? "Grid On" : "Grid Off" }), _jsx("button", { className: `viewport-panel__button ${whiteboxSnapEnabled ? "viewport-panel__button--active" : ""}`, type: "button", "data-testid": getSharedControlTestId(panelId, isActive, "whitebox-snap-toggle"), "aria-pressed": whiteboxSnapEnabled, onClick: onWhiteboxSnapToggle, children: whiteboxSnapEnabled ? "Snap On" : "Snap Off" }), _jsxs("label", { className: "viewport-panel__inline-field", children: [_jsx("span", { className: "viewport-panel__inline-label", children: "Step" }), _jsx("input", { "data-testid": getSharedControlTestId(panelId, isActive, "whitebox-snap-step"), className: "text-input viewport-panel__inline-input", type: "number", min: "0.01", step: "0.1", value: whiteboxSnapStepDraft, onChange: (event) => onWhiteboxSnapStepDraftChange(event.currentTarget.value), onBlur: onWhiteboxSnapStepBlur, onKeyDown: (event) => {
if (event.key === "Enter") {
onWhiteboxSnapStepBlur();
}
} })] })] })] }) }), _jsxs("div", { className: "viewport-panel__overlay viewport-panel__overlay--left", children: [_jsxs("div", { className: "viewport-panel__control-group viewport-panel__control-group--stack", role: "group", "aria-label": "Transform operations", children: [_jsx("button", { className: `viewport-panel__button ${transformSession.kind === "active" && transformSession.operation === "translate" ? "viewport-panel__button--active" : ""}`, type: "button", "data-testid": getSharedControlTestId(panelId, isActive, "transform-translate-button"), "aria-pressed": transformSession.kind === "active" && transformSession.operation === "translate", disabled: transformButtonsDisabled || !canTranslateSelectedTarget, onClick: () => onBeginTransformOperation("translate"), children: "Move" }), _jsx("button", { className: `viewport-panel__button ${transformSession.kind === "active" && transformSession.operation === "rotate" ? "viewport-panel__button--active" : ""}`, type: "button", "data-testid": getSharedControlTestId(panelId, isActive, "transform-rotate-button"), "aria-pressed": transformSession.kind === "active" && transformSession.operation === "rotate", disabled: transformButtonsDisabled || !canRotateSelectedTarget, onClick: () => onBeginTransformOperation("rotate"), children: "Rotate" }), _jsx("button", { className: `viewport-panel__button ${transformSession.kind === "active" && transformSession.operation === "scale" ? "viewport-panel__button--active" : ""}`, type: "button", "data-testid": getSharedControlTestId(panelId, isActive, "transform-scale-button"), "aria-pressed": transformSession.kind === "active" && transformSession.operation === "scale", disabled: transformButtonsDisabled || !canScaleSelectedTarget, onClick: () => onBeginTransformOperation("scale"), children: "Scale" })] }), _jsx("div", { className: "viewport-panel__control-group viewport-panel__control-group--stack", role: "group", "aria-label": "Whitebox selection mode", children: WHITEBOX_SELECTION_MODES.map((mode) => (_jsx("button", { className: `viewport-panel__button ${whiteboxSelectionMode === mode ? "viewport-panel__button--active" : ""}`, type: "button", "data-testid": getSharedControlTestId(panelId, isActive, `whitebox-selection-mode-${mode}`), "aria-pressed": whiteboxSelectionMode === mode, onClick: () => onWhiteboxSelectionModeChange(mode), children: getWhiteboxSelectionModeLabel(mode) }, mode))) })] })] }));
}