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 = {
|
const testCase = {
|
||||||
testDescription: 'Right view',
|
testDescription: 'Right view',
|
||||||
expectedCameraPosition: { x: 5660.02, y: -152, z: 26 },
|
expectedCameraPosition: { x: 5660.02, y: -152, z: 26 },
|
||||||
@ -7698,6 +7698,16 @@ test.describe('Testing Gizmo', () => {
|
|||||||
testCase.expectedCameraTarget.z.toString()
|
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 'clientSideScene/sceneInfra'
|
||||||
import { sceneInfra } from 'lib/singletons'
|
import { sceneInfra } from 'lib/singletons'
|
||||||
import { MutableRefObject, useEffect, useRef } from 'react'
|
import { MutableRefObject, useEffect, useMemo, useRef } from 'react'
|
||||||
import {
|
import {
|
||||||
WebGLRenderer,
|
WebGLRenderer,
|
||||||
Scene,
|
Scene,
|
||||||
@ -25,6 +25,8 @@ import {
|
|||||||
ContextMenuItem,
|
ContextMenuItem,
|
||||||
ContextMenuItemRefresh,
|
ContextMenuItemRefresh,
|
||||||
} from './ContextMenu'
|
} from './ContextMenu'
|
||||||
|
import { Popover } from '@headlessui/react'
|
||||||
|
import { CustomIcon } from './CustomIcon'
|
||||||
|
|
||||||
const CANVAS_SIZE = 80
|
const CANVAS_SIZE = 80
|
||||||
const FRUSTUM_SIZE = 0.5
|
const FRUSTUM_SIZE = 0.5
|
||||||
@ -59,6 +61,30 @@ export default function Gizmo() {
|
|||||||
const raycasterIntersect = useRef<Intersection<Object3D> | null>(null)
|
const raycasterIntersect = useRef<Intersection<Object3D> | null>(null)
|
||||||
const cameraPassiveUpdateTimer = useRef(0)
|
const cameraPassiveUpdateTimer = useRef(0)
|
||||||
const raycasterPassiveUpdateTimer = 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(() => {
|
useEffect(() => {
|
||||||
if (!canvasRef.current) return
|
if (!canvasRef.current) return
|
||||||
@ -115,43 +141,48 @@ export default function Gizmo() {
|
|||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<div className="relative">
|
||||||
<div
|
<div
|
||||||
ref={wrapperRef}
|
ref={wrapperRef}
|
||||||
aria-label="View orientation gizmo"
|
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"
|
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} />
|
<canvas ref={canvasRef} />
|
||||||
<ContextMenu
|
<ContextMenu menuTargetElement={wrapperRef} items={menuItems} />
|
||||||
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 />,
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
</div>
|
</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