Skip to content
This repository has been archived by the owner on Aug 18, 2024. It is now read-only.

Commit

Permalink
rework routes into categories
Browse files Browse the repository at this point in the history
  • Loading branch information
HexaField committed Jul 4, 2024
1 parent 3ba27d7 commit e302bc8
Show file tree
Hide file tree
Showing 2 changed files with 109 additions and 105 deletions.
83 changes: 48 additions & 35 deletions src/examplesRoute.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,56 +5,69 @@ import AvatarMocapEntry from './examples/avatarMocap'
import AvatarTestEntry from './examples/avatarTest'
import ComponentExamplesRoute, { subComponentExamples } from './examples/componentExamples/componentExamples'
import GLTFViewer from './examples/gltf'
import ImmersiveAR from './examples/immersiveAR'
import ImmersiveVR from './examples/immersiveVR'
import MultipleScenesEntry from './examples/multipleScenes'
import Routes, { RouteData } from './sceneRoute'
import ImmersiveAR from './examples/immersiveAR'
import Routes, { RouteCategories } from './sceneRoute'

export const examples: RouteData[] = [
export const examples: RouteCategories = [
{
category: 'WebXR',
routes: [
{
name: 'Immersive AR',
description: 'Immersive AR example',
entry: ImmersiveAR
},
{
name: 'Immersive VR',
description: 'Immersive VR example',
entry: ImmersiveVR
}
]
},
{
name: 'Components Example',
description: 'Component examples',
entry: ComponentExamplesRoute,
sub: subComponentExamples.map((sub) => ({
category: 'Components',
routes: subComponentExamples.map((sub) => ({
name: sub.name,
description: sub.description,
props: { Reactor: sub.Reactor }
entry: () => <ComponentExamplesRoute Reactor={sub.Reactor} />
}))
},
{
name: 'Avatar Mocap',
description: 'Avatar mocap example',
entry: AvatarMocapEntry
},
{
name: 'Avatar Test',
description: 'Load many avatars',
entry: AvatarTestEntry
},
{
name: 'GLTF Viewer',
description: 'Drag and drop GLTF files',
entry: GLTFViewer
},
{
name: 'Multiple Scenes',
description: 'multiple scenes example',
entry: MultipleScenesEntry
},
{
name: 'Immersive AR',
description: 'Immersive AR example',
entry: ImmersiveAR
category: 'Avatar',
routes: [
{
name: 'Mocap',
description: 'Avatar mocap example',
entry: AvatarMocapEntry
},
{
name: 'Test',
description: 'Load many avatars',
entry: AvatarTestEntry
}
]
},
{
name: 'Immersive VR',
description: 'Immersive VR example',
entry: ImmersiveVR
category: 'Scene',
routes: [
{
name: 'GLTF Viewer',
description: 'Drag and drop GLTF files',
entry: GLTFViewer
},
{
name: 'Multiple',
description: 'multiple scenes example',
entry: MultipleScenesEntry
}
]
}
]

const ExampleRoutes = () => {
return <Routes routes={examples} header="Examples" />
return <Routes routeCategories={examples} header="Examples" />
}

export default ExampleRoutes
131 changes: 61 additions & 70 deletions src/sceneRoute.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
// @ts-ignore
import styles from './sceneRoute.css?inline'

import React, { useEffect, useRef, useState } from 'react'
import React, { useEffect } from 'react'

import { SearchParamState } from '@etherealengine/client-core/src/common/services/RouterService'
import { useLoadEngineWithScene, useNetwork } from '@etherealengine/client-core/src/components/World/EngineHooks'
import { useLoadScene } from '@etherealengine/client-core/src/components/World/LoadLocationScene'
import { useEngineCanvas } from '@etherealengine/client-core/src/hooks/useEngineCanvas'
Expand All @@ -11,15 +12,20 @@ import { staticResourcePath } from '@etherealengine/common/src/schema.type.modul
import { Entity, getComponent, setComponent } from '@etherealengine/ecs'
import '@etherealengine/engine/src/EngineModule'
import { GLTFAssetState } from '@etherealengine/engine/src/gltf/GLTFState'
import { useHookstate, useImmediateEffect, useMutableState } from '@etherealengine/hyperflux'
import {
getMutableState,
useHookstate,
useImmediateEffect,
useMutableState,
useReactiveRef
} from '@etherealengine/hyperflux'
import { EngineState } from '@etherealengine/spatial/src/EngineState'
import { CameraComponent } from '@etherealengine/spatial/src/camera/components/CameraComponent'
import { CameraOrbitComponent } from '@etherealengine/spatial/src/camera/components/CameraOrbitComponent'
import { useFind } from '@etherealengine/spatial/src/common/functions/FeathersHooks'
import { InputComponent } from '@etherealengine/spatial/src/input/components/InputComponent'
import Button from '@etherealengine/ui/src/primitives/tailwind/Button'
import { HiChevronDoubleLeft, HiChevronDoubleRight } from 'react-icons/hi2'
import { SearchParamState } from '@etherealengine/client-core/src/common/services/RouterService'

type Metadata = {
name: string
Expand All @@ -35,6 +41,8 @@ export type RouteData = Metadata & {
sub?: SubRoute[]
}

export type RouteCategories = Array<{ category: string; routes: RouteData[] }>

export const buttonStyle = {
width: 'auto',
height: '100%',
Expand All @@ -59,6 +67,7 @@ const Header = (props: { header: string }) => {
}

export const useRouteScene = (projectName = 'ee-development-test-suite', sceneName = 'public/scenes/Examples.gltf') => {
const viewerEntity = useMutableState(EngineState).viewerEntity.value
useLoadScene({ projectName: projectName, sceneName: sceneName })
useNetwork({ online: false })
useLoadEngineWithScene()
Expand All @@ -67,7 +76,6 @@ export const useRouteScene = (projectName = 'ee-development-test-suite', sceneNa

const gltfState = useMutableState(GLTFAssetState)
const sceneEntity = useHookstate<undefined | Entity>(undefined)
const viewerEntity = useMutableState(EngineState).viewerEntity.value

useEffect(() => {
if (!assetQuery.data[0]) return
Expand All @@ -83,61 +91,37 @@ export const useRouteScene = (projectName = 'ee-development-test-suite', sceneNa
setComponent(viewerEntity, InputComponent)
getComponent(viewerEntity, CameraComponent).position.set(0, 3, 4)

// SearchParamState.set('spectate', '')
SearchParamState.set('spectate', '')
}, [viewerEntity])

return sceneEntity
}

const routeKey = 'route'
const subRouteKey = 'subroute'
const getPathForRoute = (category: string, name: string) => {
return (category.toLowerCase() + '_' + name.toLocaleLowerCase()).replace(' ', '_')
}

const Routes = (props: { routes: RouteData[]; header: string }) => {
const { routes, header } = props
const [currentRoute, setCurrentRoute] = useState(null as null | number)
const [currentSubRoute, setCurrentSubRoute] = useState(0)
const Routes = (props: { routeCategories: RouteCategories; header: string }) => {
const { routeCategories, header } = props
const currentRoute = useMutableState(SearchParamState).example.value
const categoriesShown = useHookstate({} as Record<string, boolean>)
const hidden = useHookstate(false)

const ref = useRef(null as null | HTMLDivElement)
const [ref, setRef] = useReactiveRef<HTMLDivElement>()

useEngineCanvas(ref)

const onClick = (routeIndex: number) => {
setCurrentRoute(routeIndex)
setCurrentSubRoute(0)
}
const viewerEntity = useHookstate(getMutableState(EngineState).viewerEntity).value

const onSubClick = (subIndex: number) => {
setCurrentSubRoute(subIndex)
const onClick = (category: string, route: string) => {
SearchParamState.set('example', getPathForRoute(category, route))
}

useEffect(() => {
const queryString = window.location.search
const urlParams = new URLSearchParams(queryString)
const routeIndexStr = urlParams.get(routeKey) as any
if (routeIndexStr) {
const routeIndex = Number(routeIndexStr)
setCurrentRoute(routeIndex)
const subIndexStr = urlParams.get(subRouteKey) as any
if (subIndexStr) {
const subIndex = Number(subIndexStr)
setCurrentSubRoute(subIndex)
}
}
}, [])
const selectedRoute = routeCategories.flatMap((route) =>
route.routes.filter((r) => getPathForRoute(route.category, r.name) === currentRoute)
)[0]

useEffect(() => {
if (currentRoute === null) return
const url = new URL(window.location.href)
url.searchParams.set(routeKey, currentRoute.toString())
url.searchParams.set(subRouteKey, currentSubRoute.toString())
window.history.pushState(null, '', url.toString())
}, [currentRoute, currentSubRoute])

const selectedRoute = currentRoute !== null ? routes[currentRoute] : null
const selectedSub = selectedRoute && selectedRoute.sub && selectedRoute.sub[currentSubRoute]
const Entry = selectedRoute && selectedRoute.entry
const subProps = selectedSub ? selectedSub.props : {}

return (
<>
Expand All @@ -160,43 +144,50 @@ const Routes = (props: { routes: RouteData[]; header: string }) => {
<div className="NavBarContainer" style={{ zIndex: '100', width: hidden.value ? '0%' : '' }}>
<Header header={header} />
<div className="NavBarSelectionContainer">
{routes.map((route, index) => {
const title = route.name
const desc = route.description
{routeCategories.map((category, index) => {
const categoryShown = categoriesShown[category.category]
return (
<React.Fragment key={title}>
<div
className={index === currentRoute ? 'SelectedItemContainer' : 'RouteItemContainer'}
onClick={() => onClick(index)}
>
<div className="RouteItemTitle">{title}</div>
<div className="RouteItemDescription">{desc}</div>
<React.Fragment key={category.category}>
<div className="flex flex-row text-white">
{category.category}
<Button
className="z-10 m-1 px-0"
rounded="full"
variant="outline"
onClick={() => categoryShown.set(!categoryShown.value)}
startIcon={
categoryShown.value ? (
<HiChevronDoubleRight className="pointer-events-none place-self-center text-theme-primary" />
) : (
<HiChevronDoubleLeft className="pointer-events-none place-self-center text-theme-primary" />
)
}
/>
</div>
{index === currentRoute && routes[currentRoute]?.sub && (
<div className="SubItemsContainer">
{routes[currentRoute].sub?.map((sub, subIndex) => {
const subTitle = sub.name
const subDesc = sub.description
return (
{categoryShown.value &&
category.routes.map((route, index) => {
const title = route.name
const desc = route.description
const path = getPathForRoute(category.category, title)
return (
<React.Fragment key={title}>
<div
key={subTitle}
className={subIndex === currentSubRoute ? 'SelectedItemContainer' : 'RouteItemContainer'}
onClick={() => onSubClick(subIndex)}
className={path === currentRoute ? 'SelectedItemContainer' : 'RouteItemContainer'}
onClick={() => onClick(category.category, title)}
>
<div className="RouteItemTitle">{subTitle}</div>
<div className="RouteItemDescription">{subDesc}</div>
<div className="RouteItemTitle">{title}</div>
<div className="RouteItemDescription">{desc}</div>
</div>
)
})}
</div>
)}
</React.Fragment>
)
})}
</React.Fragment>
)
})}
</div>
</div>
<div id="examples-panel" ref={ref} style={{ flexGrow: 1, pointerEvents: 'none' }} />
{Entry && <Entry {...subProps} />}
<div id="examples-panel" ref={setRef} style={{ flexGrow: 1, pointerEvents: 'none' }} />
{viewerEntity && Entry && <Entry />}
</div>
</>
)
Expand Down

0 comments on commit e302bc8

Please sign in to comment.