Add a little dropdown arrow menu to gizmo with view settings (#3300)
This commit is contained in:
@ -7603,7 +7603,7 @@ test.describe('Testing Gizmo', () => {
|
||||
})
|
||||
}
|
||||
|
||||
test('Context menu', async ({ page }) => {
|
||||
test('Context menu and popover menu', async ({ page }) => {
|
||||
const testCase = {
|
||||
testDescription: 'Right view',
|
||||
expectedCameraPosition: { x: 5660.02, y: -152, z: 26 },
|
||||
@ -7698,6 +7698,16 @@ test.describe('Testing Gizmo', () => {
|
||||
testCase.expectedCameraTarget.z.toString()
|
||||
),
|
||||
])
|
||||
|
||||
// Now test the popover menu.
|
||||
// It has the same click handlers, so we can just
|
||||
// test that it opens and contains the same content.
|
||||
const gizmoPopoverButton = page.getByRole('button', {
|
||||
name: 'view settings',
|
||||
})
|
||||
await expect(gizmoPopoverButton).toBeVisible()
|
||||
await gizmoPopoverButton.click()
|
||||
await expect(buttonToTest).toBeVisible()
|
||||
})
|
||||
})
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { SceneInfra } from 'clientSideScene/sceneInfra'
|
||||
import { sceneInfra } from 'lib/singletons'
|
||||
import { MutableRefObject, useEffect, useRef } from 'react'
|
||||
import { MutableRefObject, useEffect, useMemo, useRef } from 'react'
|
||||
import {
|
||||
WebGLRenderer,
|
||||
Scene,
|
||||
@ -25,6 +25,8 @@ import {
|
||||
ContextMenuItem,
|
||||
ContextMenuItemRefresh,
|
||||
} from './ContextMenu'
|
||||
import { Popover } from '@headlessui/react'
|
||||
import { CustomIcon } from './CustomIcon'
|
||||
|
||||
const CANVAS_SIZE = 80
|
||||
const FRUSTUM_SIZE = 0.5
|
||||
@ -59,6 +61,30 @@ export default function Gizmo() {
|
||||
const raycasterIntersect = useRef<Intersection<Object3D> | null>(null)
|
||||
const cameraPassiveUpdateTimer = useRef(0)
|
||||
const raycasterPassiveUpdateTimer = useRef(0)
|
||||
const menuItems = useMemo(
|
||||
() => [
|
||||
...Object.entries(axisNamesSemantic).map(([axisName, axisSemantic]) => (
|
||||
<ContextMenuItem
|
||||
key={axisName}
|
||||
onClick={() => {
|
||||
sceneInfra.camControls.updateCameraToAxis(axisName as AxisNames)
|
||||
}}
|
||||
>
|
||||
{axisSemantic} view
|
||||
</ContextMenuItem>
|
||||
)),
|
||||
<ContextMenuItem
|
||||
onClick={() => {
|
||||
sceneInfra.camControls.resetCameraPosition()
|
||||
}}
|
||||
>
|
||||
Reset view
|
||||
</ContextMenuItem>,
|
||||
<ContextMenuDivider />,
|
||||
<ContextMenuItemRefresh />,
|
||||
],
|
||||
[axisNamesSemantic]
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
if (!canvasRef.current) return
|
||||
@ -115,43 +141,48 @@ export default function Gizmo() {
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="relative">
|
||||
<div
|
||||
ref={wrapperRef}
|
||||
aria-label="View orientation gizmo"
|
||||
className="grid place-content-center rounded-full overflow-hidden border border-solid border-primary/50 pointer-events-auto bg-chalkboard-10/70 dark:bg-chalkboard-100/80 backdrop-blur-sm"
|
||||
>
|
||||
<canvas ref={canvasRef} />
|
||||
<ContextMenu
|
||||
menuTargetElement={wrapperRef}
|
||||
items={[
|
||||
...Object.entries(axisNamesSemantic).map(
|
||||
([axisName, axisSemantic]) => (
|
||||
<ContextMenuItem
|
||||
key={axisName}
|
||||
onClick={() => {
|
||||
sceneInfra.camControls.updateCameraToAxis(
|
||||
axisName as AxisNames
|
||||
)
|
||||
}}
|
||||
>
|
||||
{axisSemantic} view
|
||||
</ContextMenuItem>
|
||||
)
|
||||
),
|
||||
<ContextMenuItem
|
||||
onClick={() => {
|
||||
sceneInfra.camControls.resetCameraPosition()
|
||||
}}
|
||||
>
|
||||
Reset view
|
||||
</ContextMenuItem>,
|
||||
<ContextMenuDivider />,
|
||||
<ContextMenuItemRefresh />,
|
||||
]}
|
||||
/>
|
||||
<ContextMenu menuTargetElement={wrapperRef} items={menuItems} />
|
||||
</div>
|
||||
<GizmoDropdown items={menuItems} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function GizmoDropdown({ items }: { items: React.ReactNode[] }) {
|
||||
return (
|
||||
<Popover className="absolute top-0 right-0 pointer-events-auto">
|
||||
{({ close }) => (
|
||||
<>
|
||||
<Popover.Button className="border-none p-0 m-0 -translate-y-1/4 translate-x-1/4">
|
||||
<CustomIcon
|
||||
name="caretDown"
|
||||
className="w-4 h-4 ui-open:rotate-180"
|
||||
/>
|
||||
<span className="sr-only">View settings</span>
|
||||
</Popover.Button>
|
||||
<Popover.Panel
|
||||
className={`absolute bottom-full right-0 mb-2 w-48 bg-chalkboard-10 dark:bg-chalkboard-90
|
||||
border border-solid border-chalkboard-10 dark:border-chalkboard-90 rounded
|
||||
shadow-lg`}
|
||||
>
|
||||
<ul className="relative flex flex-col items-stretch content-stretch p-0.5">
|
||||
{items.map((item, index) => (
|
||||
<li key={index} className="contents" onClick={() => close()}>
|
||||
{item}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</Popover.Panel>
|
||||
</>
|
||||
)}
|
||||
</Popover>
|
||||
)
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user