Add a little dropdown arrow menu to gizmo with view settings (#3300)

This commit is contained in:
Frank Noirot
2024-08-06 10:01:55 -04:00
committed by GitHub
parent a3ff0a45eb
commit c843dfad95
2 changed files with 73 additions and 32 deletions

View File

@ -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>
)
}