State controlled tray mvp
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
import Svg from 'src/components/Svg/Svg'
|
import Svg from 'src/components/Svg/Svg'
|
||||||
import { sidebarTopConfig, sidebarBottomConfig, sidebarCombinedConfig } from './sidebarConfig'
|
import { sidebarTopConfig, sidebarBottomConfig, sidebarCombinedConfig } from './sidebarConfig'
|
||||||
|
import { useIdeContext } from 'src/helpers/hooks/useIdeContext'
|
||||||
|
|
||||||
function TabToggle({ item, className = "", active, onChange, onClick }) {
|
function TabToggle({ item, className = "", active, onChange, onClick }) {
|
||||||
return (
|
return (
|
||||||
@@ -20,29 +21,24 @@ function TabToggle({ item, className = "", active, onChange, onClick }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const IdeSideBar = () => {
|
const IdeSideBar = () => {
|
||||||
const [selectedTab, setSelectedTab] = useState("")
|
const { state, thunkDispatch } = useIdeContext()
|
||||||
const [lastOpen, setLastOpen] = useState("")
|
|
||||||
|
|
||||||
function onTabClick(name) {
|
function onTabClick(name) {
|
||||||
return function() {
|
return function() {
|
||||||
if (selectedTab === name) {
|
thunkDispatch({type: 'settingsButtonClicked', payload: [name]})
|
||||||
setLastOpen(selectedTab)
|
|
||||||
setSelectedTab("")
|
|
||||||
} else if (selectedTab === "" && lastOpen === name) {
|
|
||||||
setSelectedTab(name)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
const selectedTab = React.useMemo(() => sidebarCombinedConfig.find(item => item.name === state.sideTray[0]), [state.sideTray])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="flex h-full bg-ch-gray-900">
|
<section className="flex h-full bg-ch-gray-900 border border-red-500">
|
||||||
<fieldset className="h-full flex flex-col justify-between bg-ch-gray-700">
|
<fieldset className="h-full flex flex-col justify-between bg-ch-gray-700">
|
||||||
<div>
|
<div>
|
||||||
{ sidebarTopConfig.map((topItem, i) => (
|
{ sidebarTopConfig.map((topItem, i) => (
|
||||||
<TabToggle
|
<TabToggle
|
||||||
item={topItem}
|
item={topItem}
|
||||||
active={ selectedTab === topItem.name }
|
active={ state.sideTray[0] === topItem.name }
|
||||||
onChange={ () => setSelectedTab(topItem.name) }
|
onChange={ () => onTabClick(topItem.name) }
|
||||||
onClick={ onTabClick(topItem.name) }
|
onClick={ onTabClick(topItem.name) }
|
||||||
key={ 'tab-' + i }
|
key={ 'tab-' + i }
|
||||||
/>
|
/>
|
||||||
@@ -52,18 +48,18 @@ const IdeSideBar = () => {
|
|||||||
{ sidebarBottomConfig.map((bottomItem, i) => (
|
{ sidebarBottomConfig.map((bottomItem, i) => (
|
||||||
<TabToggle
|
<TabToggle
|
||||||
item={bottomItem}
|
item={bottomItem}
|
||||||
active={ selectedTab === bottomItem.name }
|
active={ state.sideTray[0] === bottomItem.name }
|
||||||
onChange={ () => setSelectedTab(bottomItem.name) }
|
onChange={ () => onTabClick(bottomItem.name) }
|
||||||
onClick={ onTabClick(bottomItem.name) }
|
onClick={ onTabClick(bottomItem.name) }
|
||||||
key={ 'tab-' + (sidebarTopConfig.length+i) }
|
key={ 'tab-' + (sidebarTopConfig.length+i) }
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
{ sidebarCombinedConfig.find(item => item.name === selectedTab)?.panel && (
|
{ selectedTab?.panel && (
|
||||||
<div className="w-56 bg-ch-gray-900 text-ch-gray-300 border border-ch-pink-800 border-opacity-30" style={{ height: 'calc(100% - 6px)', margin: '3px'}}>
|
<div className="w-56 bg-ch-gray-900 text-ch-gray-300 border border-ch-pink-800 border-opacity-30" style={{ height: 'calc(100% - 6px)', margin: '3px'}}>
|
||||||
<h2 className="flex items-center h-9 px-4 bg-ch-pink-800 bg-opacity-30">{ selectedTab }</h2>
|
<h2 className="flex items-center h-9 px-4 bg-ch-pink-800 bg-opacity-30">{ selectedTab.name }</h2>
|
||||||
{ sidebarCombinedConfig.find(item => item.name === selectedTab).panel }
|
{ selectedTab.panel }
|
||||||
</div>
|
</div>
|
||||||
) }
|
) }
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import React, { ReactNode } from 'react'
|
import React, { ReactNode } from 'react'
|
||||||
import { SvgNames } from 'src/components/Svg/Svg'
|
import { SvgNames } from 'src/components/Svg/Svg'
|
||||||
|
import { useIdeContext } from 'src/helpers/hooks/useIdeContext'
|
||||||
|
|
||||||
interface SidebarConfigType {
|
interface SidebarConfigType {
|
||||||
name: string,
|
name: string,
|
||||||
@@ -67,7 +68,7 @@ export const sidebarBottomConfig : SidebarConfigType[] = [
|
|||||||
name: 'Settings',
|
name: 'Settings',
|
||||||
icon: 'gear',
|
icon: 'gear',
|
||||||
disabled: false,
|
disabled: false,
|
||||||
panel: <SettingsMenu />,
|
panel: <SettingsMenu parentName="Settings"/>,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -76,15 +77,26 @@ export const sidebarCombinedConfig = [
|
|||||||
...sidebarBottomConfig,
|
...sidebarBottomConfig,
|
||||||
]
|
]
|
||||||
|
|
||||||
function SettingsMenu() {
|
function SettingsMenu({parentName}: {parentName: string}) {
|
||||||
|
const { state, thunkDispatch } = useIdeContext()
|
||||||
return (
|
return (
|
||||||
<article className="">
|
<article className="">
|
||||||
{ settingsConfig.map(item => (
|
{ settingsConfig.map(item => (
|
||||||
<details key={'settings-tray-'+item.name}>
|
<li className="list-none" key={'settings-tray-'+item.name}>
|
||||||
<summary className="px-2 py-2 bg-ch-pink-800 bg-opacity-10 my-px">{ item.title }</summary>
|
<button className="px-2 py-2 bg-ch-pink-800 bg-opacity-10 my-px" onClick={() => {
|
||||||
{ item.content }
|
console.log('i was clicked')
|
||||||
</details>
|
thunkDispatch((dispatch) => dispatch({type: 'settingsButtonClicked', payload:[parentName, item.name]}))
|
||||||
|
}} >{ item.title }</button>
|
||||||
|
{ state.sideTray.slice(-1)[0] === item.name && item.content }
|
||||||
|
</li>
|
||||||
|
// <details key={'settings-tray-'+item.name} open={state.sideTray.slice(-1)[0] === item.name}>
|
||||||
|
// <summary className="px-2 py-2 bg-ch-pink-800 bg-opacity-10 my-px"
|
||||||
|
// onClick={() => thunkDispatch((dispatch) => dispatch({type: 'settingsButtonClicked', payload:[parentName, item.name]}))}
|
||||||
|
// >{ item.title }hi there</summary>
|
||||||
|
// { state.sideTray.slice(-1)[0] === item.name && item.content }
|
||||||
|
// {state.sideTray.slice(-1)[0]}
|
||||||
|
// </details>
|
||||||
))}
|
))}
|
||||||
</article>
|
</article>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -115,6 +115,7 @@ export interface State {
|
|||||||
viewerSize: { width: number; height: number }
|
viewerSize: { width: number; height: number }
|
||||||
isLoading: boolean
|
isLoading: boolean
|
||||||
threeInstance: RootState
|
threeInstance: RootState
|
||||||
|
sideTray: string[] // could probably be an array of a union type
|
||||||
}
|
}
|
||||||
|
|
||||||
const code = ''
|
const code = ''
|
||||||
@@ -146,103 +147,118 @@ export const initialState: State = {
|
|||||||
viewerSize: { width: 0, height: 0 },
|
viewerSize: { width: 0, height: 0 },
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
threeInstance: null,
|
threeInstance: null,
|
||||||
|
sideTray: [],
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useIdeState = (): [State, (actionOrThunk: any) => any] => {
|
const reducer = (state: State, { type, payload }): State => {
|
||||||
const reducer = (state: State, { type, payload }): State => {
|
console.log('reducing')
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'initIde':
|
case 'initIde':
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
code:
|
||||||
|
payload.code ||
|
||||||
|
// localStorage.getItem(makeCodeStoreKey(payload.cadPackage)) ||
|
||||||
|
initCodeMap[payload.cadPackage] ||
|
||||||
|
'',
|
||||||
|
ideType: payload.cadPackage,
|
||||||
|
}
|
||||||
|
case 'updateCode':
|
||||||
|
return { ...state, code: payload }
|
||||||
|
case 'healthyRender':
|
||||||
|
const customizerParams: CadhubParams[] = payload?.customizerParams?.length
|
||||||
|
? payload.customizerParams
|
||||||
|
: state.customizerParams
|
||||||
|
const currentParameters = {}
|
||||||
|
customizerParams.forEach((param) => {
|
||||||
|
currentParameters[param.name] =
|
||||||
|
typeof state?.currentParameters?.[param.name] !== 'undefined'
|
||||||
|
? state?.currentParameters?.[param.name]
|
||||||
|
: param.initial
|
||||||
|
})
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
objectData: {
|
||||||
|
...state.objectData,
|
||||||
|
type: payload.objectData?.type,
|
||||||
|
data: payload.objectData?.data,
|
||||||
|
},
|
||||||
|
customizerParams,
|
||||||
|
currentParameters,
|
||||||
|
consoleMessages: payload.message
|
||||||
|
? [...state.consoleMessages, payload.message]
|
||||||
|
: payload.message,
|
||||||
|
isLoading: false,
|
||||||
|
}
|
||||||
|
case 'errorRender':
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
consoleMessages: payload.message
|
||||||
|
? [...state.consoleMessages, payload.message]
|
||||||
|
: payload.message,
|
||||||
|
isLoading: false,
|
||||||
|
}
|
||||||
|
case 'setCurrentCustomizerParams':
|
||||||
|
if (!Object.keys(payload || {}).length) return state
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
currentParameters: payload,
|
||||||
|
}
|
||||||
|
case 'setLayout':
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
layout: payload.message,
|
||||||
|
}
|
||||||
|
case 'updateCamera':
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
camera: payload.camera,
|
||||||
|
}
|
||||||
|
case 'updateViewerSize':
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
viewerSize: payload.viewerSize,
|
||||||
|
}
|
||||||
|
case 'setLoading':
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
isLoading: true,
|
||||||
|
}
|
||||||
|
case 'resetLoading':
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
isLoading: false,
|
||||||
|
}
|
||||||
|
case 'resetLayout':
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
layout: initialLayout,
|
||||||
|
}
|
||||||
|
case 'setThreeInstance':
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
threeInstance: payload,
|
||||||
|
}
|
||||||
|
case 'settingsButtonClicked':
|
||||||
|
const isReClick =
|
||||||
|
state.sideTray.length &&
|
||||||
|
state.sideTray.length === payload.length &&
|
||||||
|
state.sideTray.every((original, index) => original === payload[index])
|
||||||
|
if (isReClick) {
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
code:
|
sideTray: state.sideTray.slice(0, -1),
|
||||||
payload.code ||
|
|
||||||
// localStorage.getItem(makeCodeStoreKey(payload.cadPackage)) ||
|
|
||||||
initCodeMap[payload.cadPackage] ||
|
|
||||||
'',
|
|
||||||
ideType: payload.cadPackage,
|
|
||||||
}
|
}
|
||||||
case 'updateCode':
|
}
|
||||||
return { ...state, code: payload }
|
return {
|
||||||
case 'healthyRender':
|
...state,
|
||||||
const customizerParams: CadhubParams[] = payload?.customizerParams
|
sideTray: payload,
|
||||||
?.length
|
}
|
||||||
? payload.customizerParams
|
default:
|
||||||
: state.customizerParams
|
return state
|
||||||
const currentParameters = {}
|
|
||||||
customizerParams.forEach((param) => {
|
|
||||||
currentParameters[param.name] =
|
|
||||||
typeof state?.currentParameters?.[param.name] !== 'undefined'
|
|
||||||
? state?.currentParameters?.[param.name]
|
|
||||||
: param.initial
|
|
||||||
})
|
|
||||||
return {
|
|
||||||
...state,
|
|
||||||
objectData: {
|
|
||||||
...state.objectData,
|
|
||||||
type: payload.objectData?.type,
|
|
||||||
data: payload.objectData?.data,
|
|
||||||
},
|
|
||||||
customizerParams,
|
|
||||||
currentParameters,
|
|
||||||
consoleMessages: payload.message
|
|
||||||
? [...state.consoleMessages, payload.message]
|
|
||||||
: payload.message,
|
|
||||||
isLoading: false,
|
|
||||||
}
|
|
||||||
case 'errorRender':
|
|
||||||
return {
|
|
||||||
...state,
|
|
||||||
consoleMessages: payload.message
|
|
||||||
? [...state.consoleMessages, payload.message]
|
|
||||||
: payload.message,
|
|
||||||
isLoading: false,
|
|
||||||
}
|
|
||||||
case 'setCurrentCustomizerParams':
|
|
||||||
if (!Object.keys(payload || {}).length) return state
|
|
||||||
return {
|
|
||||||
...state,
|
|
||||||
currentParameters: payload,
|
|
||||||
}
|
|
||||||
case 'setLayout':
|
|
||||||
return {
|
|
||||||
...state,
|
|
||||||
layout: payload.message,
|
|
||||||
}
|
|
||||||
case 'updateCamera':
|
|
||||||
return {
|
|
||||||
...state,
|
|
||||||
camera: payload.camera,
|
|
||||||
}
|
|
||||||
case 'updateViewerSize':
|
|
||||||
return {
|
|
||||||
...state,
|
|
||||||
viewerSize: payload.viewerSize,
|
|
||||||
}
|
|
||||||
case 'setLoading':
|
|
||||||
return {
|
|
||||||
...state,
|
|
||||||
isLoading: true,
|
|
||||||
}
|
|
||||||
case 'resetLoading':
|
|
||||||
return {
|
|
||||||
...state,
|
|
||||||
isLoading: false,
|
|
||||||
}
|
|
||||||
case 'resetLayout':
|
|
||||||
return {
|
|
||||||
...state,
|
|
||||||
layout: initialLayout,
|
|
||||||
}
|
|
||||||
case 'setThreeInstance':
|
|
||||||
return {
|
|
||||||
...state,
|
|
||||||
threeInstance: payload,
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return state
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
export const useIdeState = (): [State, (actionOrThunk: any) => any] => {
|
||||||
const [state, dispatch] = useReducer(reducer, initialState)
|
const [state, dispatch] = useReducer(reducer, initialState)
|
||||||
mutableState = state
|
mutableState = state
|
||||||
const getState = (): State => mutableState
|
const getState = (): State => mutableState
|
||||||
|
|||||||
Reference in New Issue
Block a user