use redux to manage dialog
parent
69b2fff6d1
commit
53ad7c97e9
|
@ -153,6 +153,7 @@ class Sketch {
|
||||||
|
|
||||||
|
|
||||||
activate() {
|
activate() {
|
||||||
|
console.log('activatee')
|
||||||
window.addEventListener('keydown', this.onKeyPress)
|
window.addEventListener('keydown', this.onKeyPress)
|
||||||
this.canvas.addEventListener('pointerdown', this.onPick)
|
this.canvas.addEventListener('pointerdown', this.onPick)
|
||||||
this.canvas.addEventListener('pointermove', this.onHover)
|
this.canvas.addEventListener('pointermove', this.onHover)
|
||||||
|
|
|
@ -10,8 +10,8 @@ body {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
font-family: sans-serif;
|
font-family: sans-serif;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
--topNavH: 48px;
|
--topNavH: 3rem;
|
||||||
--sideNavW: 240px;
|
--sideNavW: 15rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
#c {
|
#c {
|
||||||
|
@ -38,8 +38,10 @@ body {
|
||||||
|
|
||||||
.dialog {
|
.dialog {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: var(--topNavH);
|
height: var(--topNavH);
|
||||||
left: var(--sideNavW);
|
left:0;
|
||||||
|
right:0;
|
||||||
|
top:0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -2,15 +2,15 @@
|
||||||
import ReactDOM from 'react-dom'
|
import ReactDOM from 'react-dom'
|
||||||
import React, { useState } from 'react'
|
import React, { useState } from 'react'
|
||||||
|
|
||||||
import { createStore, applyMiddleware} from 'redux'
|
import { createStore, applyMiddleware } from 'redux'
|
||||||
import { Provider } from 'react-redux'
|
import { Provider } from 'react-redux'
|
||||||
import { reducer } from './reducer'
|
import { reducer } from './reducer'
|
||||||
import logger from 'redux-logger'
|
import logger from 'redux-logger'
|
||||||
|
|
||||||
import { Tree } from './tree'
|
import { Tree } from './tree'
|
||||||
import { NavBar } from './navBar'
|
import { NavBar } from './navBar'
|
||||||
import { ToolTip} from './toolTip'
|
import { ToolTip } from './toolTip'
|
||||||
import { Dialog } from './dialog'
|
|
||||||
|
|
||||||
import './app.css'
|
import './app.css'
|
||||||
|
|
||||||
|
@ -31,14 +31,15 @@ const preloadedState = {
|
||||||
const store = createStore(reducer, {}, applyMiddleware(logger))
|
const store = createStore(reducer, {}, applyMiddleware(logger))
|
||||||
// const store = createStore(reducer, sc.loadState(), applyMiddleware(logger))
|
// const store = createStore(reducer, sc.loadState(), applyMiddleware(logger))
|
||||||
|
|
||||||
|
|
||||||
const App = ({ store }) => {
|
const App = ({ store }) => {
|
||||||
const [dialog, setDialog] = useState()
|
|
||||||
|
|
||||||
return <Provider store={store}>
|
return <Provider store={store}>
|
||||||
<NavBar setDialog={setDialog}/>
|
|
||||||
|
<NavBar />
|
||||||
<Tree />
|
<Tree />
|
||||||
<Dialog {...{dialog, setDialog}}/>
|
<ToolTip />
|
||||||
<ToolTip/>
|
|
||||||
</Provider>
|
</Provider>
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -3,64 +3,71 @@
|
||||||
import React, { useEffect, useReducer, useRef, useState } from 'react';
|
import React, { useEffect, useReducer, useRef, useState } from 'react';
|
||||||
|
|
||||||
import { useDispatch, useSelector } from 'react-redux'
|
import { useDispatch, useSelector } from 'react-redux'
|
||||||
|
|
||||||
import { MdDone, MdClose } from 'react-icons/md'
|
import { MdDone, MdClose } from 'react-icons/md'
|
||||||
import { GiVerticalFlip } from 'react-icons/gi'
|
|
||||||
import * as Icon from "./icons";
|
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 dispatch = useDispatch()
|
||||||
const treeEntriesById = useSelector(state => state.treeEntries.byId)
|
|
||||||
const activeSketchId = useSelector(state => state.treeEntries.activeSketchId)
|
|
||||||
|
|
||||||
const ref = useRef()
|
const ref = useRef()
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
if (!ref.current) return
|
||||||
ref.current.focus()
|
ref.current.focus()
|
||||||
}, [])
|
}, [dialog])
|
||||||
|
|
||||||
const extrude = () => {
|
const extrude = () => {
|
||||||
let sketch
|
sc.extrude(dialog.target, ref.current.value)
|
||||||
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.render()
|
sc.render()
|
||||||
forceUpdate()
|
|
||||||
}
|
|
||||||
|
|
||||||
|
dispatch({ type: "clear-dialog" })
|
||||||
|
forceUpdate()
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
const [_, forceUpdate] = useReducer(x => x + 1, 0);
|
const [_, forceUpdate] = useReducer(x => x + 1, 0);
|
||||||
|
|
||||||
return <div className='dialog w-40 h-10 flex items-center bg-gray-700'>
|
switch (dialog.action) {
|
||||||
{/* return <div className='w-40 h-full flex items-center bg-gray-700'> */}
|
case 'extrude':
|
||||||
<input className='w-1/2' type="number" {...useNumField(1)} step="0.1" ref={ref} />
|
return <>
|
||||||
<Icon.Flip className="btn w-auto h-full p-2"
|
<input className='w-1/2' type="number" defaultValue="1" step="0.1" ref={ref} />
|
||||||
onClick={() => ref.current.value *= -1}
|
<Icon.Flip className="btn w-auto h-full p-2"
|
||||||
/>
|
onClick={() => ref.current.value *= -1}
|
||||||
<MdDone
|
/>
|
||||||
className="btn w-auto h-full p-2"
|
<MdDone
|
||||||
onClick={extrude}
|
className="btn w-auto h-full p-2"
|
||||||
/>
|
onClick={extrude}
|
||||||
<MdClose className="btn w-auto h-full p-2"
|
/>
|
||||||
onClick={() => setDialog(null)}
|
<MdClose className="btn w-auto h-full p-2"
|
||||||
/>
|
onClick={() => dispatch({ type: "clear-dialog" })}
|
||||||
</div>
|
/>
|
||||||
|
</>
|
||||||
|
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 };
|
|
||||||
}
|
|
||||||
|
|
|
@ -7,12 +7,12 @@ import { useDispatch, useSelector } from 'react-redux'
|
||||||
import { FaEdit } from 'react-icons/fa'
|
import { FaEdit } from 'react-icons/fa'
|
||||||
import { MdDone, MdSave, MdFolder } from 'react-icons/md'
|
import { MdDone, MdSave, MdFolder } from 'react-icons/md'
|
||||||
import * as Icon from "./icons";
|
import * as Icon from "./icons";
|
||||||
|
import { Dialog } from './dialog'
|
||||||
|
|
||||||
|
export const NavBar = () => {
|
||||||
export const NavBar = ({ setDialog }) => {
|
|
||||||
const dispatch = useDispatch()
|
const dispatch = useDispatch()
|
||||||
const activeSketchId = useSelector(state => state.treeEntries.activeSketchId)
|
const activeSketchId = useSelector(state => state.treeEntries.activeSketchId)
|
||||||
|
const treeEntriesById = useSelector(state => state.treeEntries.byId)
|
||||||
|
|
||||||
const boolOp = (code) => {
|
const boolOp = (code) => {
|
||||||
if (sc.selected.length != 2 || !sc.selected.every(e => e.userData.type == 'mesh')) return
|
if (sc.selected.length != 2 || !sc.selected.every(e => e.userData.type == 'mesh')) return
|
||||||
|
@ -21,11 +21,11 @@ export const NavBar = ({ setDialog }) => {
|
||||||
sc.render()
|
sc.render()
|
||||||
forceUpdate()
|
forceUpdate()
|
||||||
}
|
}
|
||||||
const extrude = () => { setDialog(true) }
|
|
||||||
|
|
||||||
const addSketch = () => {
|
const addSketch = () => {
|
||||||
sc.addSketch()
|
sc.addSketch()
|
||||||
console.log(!!sc.activeSketch, 'state')
|
dispatch({ type: 'set-dialog', action: 'sketch' })
|
||||||
|
|
||||||
forceUpdate()
|
forceUpdate()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,14 +45,18 @@ export const NavBar = ({ setDialog }) => {
|
||||||
// }, [treeEntriesById])
|
// }, [treeEntriesById])
|
||||||
|
|
||||||
|
|
||||||
const btnz = [
|
const sketchModeButtons = [
|
||||||
[MdDone, () => {
|
// [MdDone, () => {
|
||||||
|
// // dispatch({ type: 'update-descendents', sketch})
|
||||||
|
|
||||||
|
// sc.activeSketch.deactivate()
|
||||||
|
// sc.render()
|
||||||
|
// forceUpdate()
|
||||||
|
// }, 'Finish'],
|
||||||
|
[Icon.Extrude, () => {
|
||||||
sc.activeSketch.deactivate()
|
sc.activeSketch.deactivate()
|
||||||
// dispatch({ type: 'update-descendents', sketch})
|
dispatch({ type: 'set-dialog', action: 'extrude', target: sc.activeSketch })
|
||||||
sc.render()
|
}, 'Extrude [e]'],
|
||||||
forceUpdate()
|
|
||||||
}, 'Finish'],
|
|
||||||
[Icon.Extrude, extrude, 'Extrude [e]'],
|
|
||||||
[Icon.Dimension, () => sc.activeSketch.command('d'), 'Dimension [d]'],
|
[Icon.Dimension, () => sc.activeSketch.command('d'), 'Dimension [d]'],
|
||||||
[Icon.Line, () => sc.activeSketch.command('l'), 'Line [l]'],
|
[Icon.Line, () => sc.activeSketch.command('l'), 'Line [l]'],
|
||||||
[Icon.Arc, () => sc.activeSketch.command('a'), 'Arc [a]'],
|
[Icon.Arc, () => sc.activeSketch.command('a'), 'Arc [a]'],
|
||||||
|
@ -63,9 +67,12 @@ export const NavBar = ({ setDialog }) => {
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
const btnz2 = [
|
const partModeButtons = [
|
||||||
[FaEdit, addSketch, 'Sketch [s]'],
|
[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.Union, () => boolOp('u'), 'Union'],
|
||||||
[Icon.Subtract, () => boolOp('s'), 'Subtract'],
|
[Icon.Subtract, () => boolOp('s'), 'Subtract'],
|
||||||
[Icon.Intersect, () => boolOp('i'), 'Intersect'],
|
[Icon.Intersect, () => boolOp('i'), 'Intersect'],
|
||||||
|
@ -77,19 +84,26 @@ export const NavBar = ({ setDialog }) => {
|
||||||
const [_, forceUpdate] = useReducer(x => x + 1, 0);
|
const [_, forceUpdate] = useReducer(x => x + 1, 0);
|
||||||
|
|
||||||
return <div className='topNav flex justify-center items-center bg-gray-700'>
|
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 ?
|
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}
|
<Icon className="btn w-auto h-full p-3.5" tooltip={txt}
|
||||||
onClick={fcn} key={idx}
|
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}
|
<Icon className="btn w-auto h-full p-3.5" tooltip={txt}
|
||||||
onClick={fcn} key={idx}
|
onClick={fcn} key={idx}
|
||||||
/>
|
/>
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
<div className='w-40 h-full flex items-center'>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
|
@ -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({
|
export const reducer = combineReducers({
|
||||||
|
ui,
|
||||||
treeEntries
|
treeEntries
|
||||||
})
|
})
|
|
@ -13,7 +13,7 @@ export const Tree = () => {
|
||||||
|
|
||||||
return <div className='sideNav flex flex-col bg-gray-800'>
|
return <div className='sideNav flex flex-col bg-gray-800'>
|
||||||
{treeEntries.allIds.map((entId, idx) => (
|
{treeEntries.allIds.map((entId, idx) => (
|
||||||
<TreeEntry key={idx} entId={entId} />
|
<TreeEntry key={idx} entId={entId}/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -28,6 +28,8 @@ const treeIcons = {
|
||||||
|
|
||||||
const TreeEntry = ({ entId }) => {
|
const TreeEntry = ({ entId }) => {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const state = useSelector(state => state.treeEntries)
|
const state = useSelector(state => state.treeEntries)
|
||||||
const treeEntries = useSelector(state => state.treeEntries.byId)
|
const treeEntries = useSelector(state => state.treeEntries.byId)
|
||||||
const dispatch = useDispatch()
|
const dispatch = useDispatch()
|
||||||
|
@ -54,6 +56,7 @@ const TreeEntry = ({ entId }) => {
|
||||||
sketch.activate()
|
sketch.activate()
|
||||||
sc.clearSelection()
|
sc.clearSelection()
|
||||||
sc.activeSketch = sketch;
|
sc.activeSketch = sketch;
|
||||||
|
dispatch({ type: 'set-dialog', action: 'sketch' })
|
||||||
sc.render()
|
sc.render()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue