use redux to manage dialog

master
howard 2021-04-16 20:54:01 -07:00
parent 69b2fff6d1
commit 53ad7c97e9
7 changed files with 114 additions and 70 deletions

View File

@ -153,6 +153,7 @@ class Sketch {
activate() {
console.log('activatee')
window.addEventListener('keydown', this.onKeyPress)
this.canvas.addEventListener('pointerdown', this.onPick)
this.canvas.addEventListener('pointermove', this.onHover)

View File

@ -10,8 +10,8 @@ body {
height: 100%;
font-family: sans-serif;
overflow: hidden;
--topNavH: 48px;
--sideNavW: 240px;
--topNavH: 3rem;
--sideNavW: 15rem;
}
#c {
@ -38,8 +38,10 @@ body {
.dialog {
position: absolute;
top: var(--topNavH);
left: var(--sideNavW);
height: var(--topNavH);
left:0;
right:0;
top:0;
}

View File

@ -2,15 +2,15 @@
import ReactDOM from 'react-dom'
import React, { useState } from 'react'
import { createStore, applyMiddleware} from 'redux'
import { createStore, applyMiddleware } from 'redux'
import { Provider } from 'react-redux'
import { reducer } from './reducer'
import logger from 'redux-logger'
import { Tree } from './tree'
import { NavBar } from './navBar'
import { ToolTip} from './toolTip'
import { Dialog } from './dialog'
import { ToolTip } from './toolTip'
import './app.css'
@ -31,14 +31,15 @@ const preloadedState = {
const store = createStore(reducer, {}, applyMiddleware(logger))
// const store = createStore(reducer, sc.loadState(), applyMiddleware(logger))
const App = ({ store }) => {
const [dialog, setDialog] = useState()
return <Provider store={store}>
<NavBar setDialog={setDialog}/>
<NavBar />
<Tree />
<Dialog {...{dialog, setDialog}}/>
<ToolTip/>
<ToolTip />
</Provider>
};

View File

@ -3,64 +3,71 @@
import React, { useEffect, useReducer, useRef, useState } from 'react';
import { useDispatch, useSelector } from 'react-redux'
import { MdDone, MdClose } from 'react-icons/md'
import { GiVerticalFlip } from 'react-icons/gi'
import * as Icon from "./icons";
export const Dialog = ({ dialog, setDialog }) => {
if (!dialog) return null
export const Dialog = () => {
const dialog = useSelector(state => state.ui.dialog)
const dispatch = useDispatch()
const treeEntriesById = useSelector(state => state.treeEntries.byId)
const activeSketchId = useSelector(state => state.treeEntries.activeSketchId)
const ref = useRef()
useEffect(() => {
if (!ref.current) return
ref.current.focus()
}, [])
}, [dialog])
const extrude = () => {
let sketch
if (sc.activeSketch) {
sketch = sc.activeSketch
} else if (sc.selected.length === 1 && sc.selected[0].userData.type == 'sketch') {
sketch = treeEntriesById[sc.selected[0].name]
} else {
console.log('invalid selection')
return
}
setDialog(null)
sc.extrude(sketch, ref.current.value)
sc.extrude(dialog.target, ref.current.value)
sc.render()
forceUpdate()
}
dispatch({ type: "clear-dialog" })
forceUpdate()
}
const [_, forceUpdate] = useReducer(x => x + 1, 0);
return <div className='dialog w-40 h-10 flex items-center bg-gray-700'>
{/* return <div className='w-40 h-full flex items-center bg-gray-700'> */}
<input className='w-1/2' type="number" {...useNumField(1)} step="0.1" ref={ref} />
<Icon.Flip className="btn w-auto h-full p-2"
onClick={() => ref.current.value *= -1}
/>
<MdDone
className="btn w-auto h-full p-2"
onClick={extrude}
/>
<MdClose className="btn w-auto h-full p-2"
onClick={() => setDialog(null)}
/>
</div>
switch (dialog.action) {
case 'extrude':
return <>
<input className='w-1/2' type="number" defaultValue="1" step="0.1" ref={ref} />
<Icon.Flip className="btn w-auto h-full p-2"
onClick={() => ref.current.value *= -1}
/>
<MdDone
className="btn w-auto h-full p-2"
onClick={extrude}
/>
<MdClose className="btn w-auto h-full p-2"
onClick={() => dispatch({ type: "clear-dialog" })}
/>
</>
case 'sketch':
return <>
<MdDone
className="btn w-auto h-full p-2"
onClick={() => {
// dispatch({ type: 'update-descendents', sketch})
sc.activeSketch.deactivate()
sc.render()
forceUpdate()
}}
/>
<MdClose className="btn w-auto h-full p-2"
onClick={() => dispatch({ type: "clear-dialog" })}
/>
</>
default:
return null
}
}
export const useNumField = (initValue = 0) => {
const [value, setValue] = useState(initValue);
const onChange = e => setValue(e.target.value);
return { value, onChange };
}

View File

@ -7,12 +7,12 @@ import { useDispatch, useSelector } from 'react-redux'
import { FaEdit } from 'react-icons/fa'
import { MdDone, MdSave, MdFolder } from 'react-icons/md'
import * as Icon from "./icons";
import { Dialog } from './dialog'
export const NavBar = ({ setDialog }) => {
export const NavBar = () => {
const dispatch = useDispatch()
const activeSketchId = useSelector(state => state.treeEntries.activeSketchId)
const treeEntriesById = useSelector(state => state.treeEntries.byId)
const boolOp = (code) => {
if (sc.selected.length != 2 || !sc.selected.every(e => e.userData.type == 'mesh')) return
@ -21,11 +21,11 @@ export const NavBar = ({ setDialog }) => {
sc.render()
forceUpdate()
}
const extrude = () => { setDialog(true) }
const addSketch = () => {
sc.addSketch()
console.log(!!sc.activeSketch, 'state')
dispatch({ type: 'set-dialog', action: 'sketch' })
forceUpdate()
}
@ -45,14 +45,18 @@ export const NavBar = ({ setDialog }) => {
// }, [treeEntriesById])
const btnz = [
[MdDone, () => {
const sketchModeButtons = [
// [MdDone, () => {
// // dispatch({ type: 'update-descendents', sketch})
// sc.activeSketch.deactivate()
// sc.render()
// forceUpdate()
// }, 'Finish'],
[Icon.Extrude, () => {
sc.activeSketch.deactivate()
// dispatch({ type: 'update-descendents', sketch})
sc.render()
forceUpdate()
}, 'Finish'],
[Icon.Extrude, extrude, 'Extrude [e]'],
dispatch({ type: 'set-dialog', action: 'extrude', target: sc.activeSketch })
}, 'Extrude [e]'],
[Icon.Dimension, () => sc.activeSketch.command('d'), 'Dimension [d]'],
[Icon.Line, () => sc.activeSketch.command('l'), 'Line [l]'],
[Icon.Arc, () => sc.activeSketch.command('a'), 'Arc [a]'],
@ -63,9 +67,12 @@ export const NavBar = ({ setDialog }) => {
]
const btnz2 = [
const partModeButtons = [
[FaEdit, addSketch, 'Sketch [s]'],
[Icon.Extrude, extrude, 'Extrude [e]'],
[Icon.Extrude, () => {
dispatch({ type: 'set-dialog', action: 'extrude', target: treeEntriesById[sc.selected[0].name] })
}, 'Extrude [e]'],
[Icon.Union, () => boolOp('u'), 'Union'],
[Icon.Subtract, () => boolOp('s'), 'Subtract'],
[Icon.Intersect, () => boolOp('i'), 'Intersect'],
@ -77,19 +84,26 @@ export const NavBar = ({ setDialog }) => {
const [_, forceUpdate] = useReducer(x => x + 1, 0);
return <div className='topNav flex justify-center items-center bg-gray-700'>
<div className='w-40 h-full flex items-center mr-12'>
<Dialog />
</div>
{
activeSketchId ?
btnz.map(([Icon, fcn, txt, shortcut], idx) => (
sketchModeButtons.map(([Icon, fcn, txt, shortcut], idx) => (
<Icon className="btn w-auto h-full p-3.5" tooltip={txt}
onClick={fcn} key={idx}
/>
))
:
btnz2.map(([Icon, fcn, txt, shortcut], idx) => (
partModeButtons.map(([Icon, fcn, txt, shortcut], idx) => (
<Icon className="btn w-auto h-full p-3.5" tooltip={txt}
onClick={fcn} key={idx}
/>
))
}
<div className='w-40 h-full flex items-center'>
</div>
</div>
}
}

View File

@ -82,9 +82,25 @@ export function treeEntries(state = defaultState, action) {
}
}
export function ui(state = {dialog:{}}, action) {
switch (action.type) {
case 'set-dialog':
return update(state, {
dialog: { $set: { target: action.target, action: action.action } },
})
case 'clear-dialog':
return update(state, {
dialog: { $set: {} },
})
default:
return state
}
}
export const reducer = combineReducers({
ui,
treeEntries
})

View File

@ -13,7 +13,7 @@ export const Tree = () => {
return <div className='sideNav flex flex-col bg-gray-800'>
{treeEntries.allIds.map((entId, idx) => (
<TreeEntry key={idx} entId={entId} />
<TreeEntry key={idx} entId={entId}/>
))}
</div>
@ -28,6 +28,8 @@ const treeIcons = {
const TreeEntry = ({ entId }) => {
const state = useSelector(state => state.treeEntries)
const treeEntries = useSelector(state => state.treeEntries.byId)
const dispatch = useDispatch()
@ -54,6 +56,7 @@ const TreeEntry = ({ entId }) => {
sketch.activate()
sc.clearSelection()
sc.activeSketch = sketch;
dispatch({ type: 'set-dialog', action: 'sketch' })
sc.render()
}