implement tooltip solution

master
howard 2021-04-07 15:50:53 -07:00
parent b54b871fa3
commit a7c211a105
13 changed files with 158 additions and 34 deletions

3
dist/index.html vendored
View File

@ -12,7 +12,8 @@
<meta property="og:image" content="" />
<link rel="apple-touch-icon" href="icon-192.png" />
<link rel="manifest" href="manifest.json" />
<link rel="stylesheet" href="index.css">
<!-- app.css references the css imported into app.jsx -->
<link rel="stylesheet" href="app.css">
<title>CAD Tool</title>
</head>

View File

@ -34,6 +34,9 @@ export class Scene {
this.store = store;
this.canvas = document.querySelector('#c');
this.rect = this.canvas.getBoundingClientRect().toJSON()
this.renderer = new THREE.WebGLRenderer({ canvas: this.canvas });
const size = 1;
@ -171,15 +174,12 @@ export class Scene {
window.id = curid
const entries = state.treeEntries.byId
console.log(entries)
for (let k in entries) {
console.log(k)
if (k[0] == 's') {
entries[k].obj3d = loader.parse(entries[k].obj3d)
this.obj3d.add(entries[k].obj3d)
entries[k] = new Sketch(this.camera, this.canvas, this.store, state.treeEntries.byId[k])
entries[k] = new Sketch(this, state.treeEntries.byId[k])
entries[k].obj3d.addEventListener('change', this.render) // !! took 3 hours to realize
} else if (k[0] == 'm') {
@ -219,6 +219,9 @@ function render() {
this.camera.left = -canvas.clientWidth / canvas.clientHeight;
this.camera.right = canvas.clientWidth / canvas.clientHeight;
this.camera.updateProjectionMatrix();
Object.assign(this.rect, this.canvas.getBoundingClientRect().toJSON())
}
this.renderer.render(this.obj3d, this.camera);
@ -268,13 +271,13 @@ async function addSketch() {
if (!references) return;
if (references[0].userData.type == 'plane') {
sketch = new Sketch(this.camera, this.canvas, this.store)
sketch = new Sketch(this)
sketch.obj3d.matrix = references[0].matrix
sketch.plane.applyMatrix4(sketch.obj3d.matrix)
sketch.obj3d.inverse = sketch.obj3d.matrix.clone().invert()
this.obj3d.add(sketch.obj3d)
} else {
sketch = new Sketch(this.camera, this.canvas, this.store)
sketch = new Sketch(this)
this.obj3d.add(sketch.obj3d)
sketch.align(
...references.map(

View File

@ -17,7 +17,7 @@ import { drawDimension, _onMoveDimension, setDimLines, updateDim } from './drawD
class Sketch {
constructor(camera, canvas, store, preload) {
constructor(scene, preload) {
// [0]:x, [1]:y, [2]:z
@ -75,9 +75,12 @@ class Sketch {
this.plane.applyMatrix4(this.obj3d.matrix)
}
this.camera = camera;
this.canvas = canvas;
this.store = store;
this.scene = scene;
this.camera = scene.camera
this.canvas = scene.canvas
this.rect = scene.rect
this.store = scene.store;
@ -348,14 +351,16 @@ class Sketch {
}
}
getLocation(e) {
raycaster.setFromCamera(
_vec2.set(
(e.clientX / window.innerWidth) * 2 - 1,
- (e.clientY / window.innerHeight) * 2 + 1
(e.clientX - this.rect.left)/ this.rect.width * 2 - 1,
- (e.clientY - this.rect.top)/ this.rect.height * 2 + 1
),
this.camera
);
raycaster.ray.intersectPlane(this.plane, _vec3).applyMatrix4(this.obj3d.inverse)
return _vec3

View File

@ -6,12 +6,13 @@ export function onHover(e) {
raycaster.setFromCamera(
new THREE.Vector2(
(e.clientX / window.innerWidth) * 2 - 1,
- (e.clientY / window.innerHeight) * 2 + 1
(e.clientX - this.rect.left)/ this.rect.width * 2 - 1,
- (e.clientY - this.rect.top)/ this.rect.height * 2 + 1
),
this.camera
);
let hoverPts;
let idx = []
@ -43,7 +44,7 @@ export function onHover(e) {
if (hoverPts.length) {
console.log(hoverPts)
// console.log(hoverPts)
// for (let i = 0; i < hoverPts.length; i++) {
// const obj = hoverPts[i].object
// if (['point', 'plane'].includes(obj.userData.type)) {

View File

@ -1,13 +1,19 @@
/* @tailwind base; */
@tailwind utilities;
/* html, */
* {
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
box-sizing: border-box;
}
body {
margin: 0;
height: 100%;
font-family: sans-serif;
overflow: hidden;
--topNavH: 36px;
--topNavH: 48px;
--sideNavW: 200px;
}
@ -45,3 +51,26 @@ body {
bg-gray-100 text-green-600
hover:bg-gray-200 hover:text-green-700;
}
.tooltip {
position: fixed;
display: block;
background-color: black;
color: #fff;
text-align: center;
border-radius: 4px;
padding: 4px;
visibility: hidden;
}
.arrow {
position: absolute;
bottom: 100%;
left: 50%;
margin-left: -6px;
border: solid 6px transparent;
border-bottom-color: black;
border-top: none;
}

View File

@ -4,28 +4,27 @@ import React from 'react'
import { createStore, applyMiddleware} from 'redux'
import { Provider } from 'react-redux'
import { reducer, preloadedState } from './reducer'
import logger from 'redux-logger'
import { Tree } from './treeEntry'
import { Tree } from './tree'
import { NavBar } from './navBar'
import { reducer, preloadedState } from './reducer'
import { ToolTip} from './toolTip'
import './app.css'
window.store = createStore(reducer, preloadedState, applyMiddleware(logger))
const store = createStore(reducer, preloadedState, applyMiddleware(logger))
const App = ({ store }) => (
<Provider store={store}>
<NavBar />
<Tree />
<ToolTip/>
</Provider>
);
document.addEventListener('DOMContentLoaded', () => {
ReactDOM.render(<App store={store} />, document.getElementById('react'));
});
window.store = store

View File

@ -1,6 +1,6 @@
import React, { useEffect, useReducer} from 'react';
import React, { useEffect, useReducer } from 'react';
import { useDispatch, useSelector } from 'react-redux'
@ -58,15 +58,12 @@ export const NavBar = () => {
const [_, forceUpdate] = useReducer(x => x + 1, 0);
return <div className='topNav flex justify-center'>
return <div className='topNav flex justify-center items-center'>
{
btnz.map(([Icon, fcn, txt], idx) => (
<div className="btn flex items-center justify-start p-1 text-lg" key={idx}
onClick={fcn}
>
<Icon className="w-6 h-6" />
<div className="ml-2" tooltip="wtf">{txt}</div>
</div>
<Icon className="btn w-auto h-full p-2" tooltip={txt}
onClick={fcn} key={idx}
/>
))
}
</div>

View File

@ -11,6 +11,9 @@ export const preloadedState = {
tree: {},
order: {},
},
ui: {
toolTipImmediate: false
}
}
export function reducer(state = {}, action) {

86
src/react/toolTip.jsx Normal file
View File

@ -0,0 +1,86 @@
import React, { useEffect, useRef, useState } from 'react';
export const ToolTip = () => {
/**
* Fires when new element is mouseovered, checks if it has a tooltip attribute
* If it does, updates and unhides tooltip element after a preset timeout.
* The timout is reset if user moves off of the tooltipped element
*
* Unfortunately, new mouseover fires for svg children, which clears the
* tooltip state. We add hacky lines labelled svg workaround to bubbleup / ignore
* svg children mouseovers. We use prevTooltip ref check if new svg
* child mouseover is novel. If it's not, we ignore the event
*/
const [state, setState] = useState(null)
const ref = useRef()
const activated = useRef(false)
const timeout = useRef(null)
const prevTooltip = useRef(null) // svg workaround
useEffect(() => {
const svgChildren = ['path', 'g', 'rect', 'circle']; // svg workaround
document.addEventListener('mouseover', (e) => {
let node = e.target;
while (svgChildren.includes(node.nodeName)) { // svg workaround
node = node.parentElement // svg workaround
} // svg workaround
const tooltip = node.getAttribute("tooltip")
if (tooltip == prevTooltip.current) return // svg workaround
prevTooltip.current = tooltip // svg workaround
clearTimeout(timeout.current)
if (tooltip) {
let { left, top, width, height } = node.getBoundingClientRect()
left = left + width / 2 - getTextWidth(tooltip) / 2 - 4 // 4 is padding
top = top + height + 6 // 6 is arrow height/width
setState(tooltip)
if (activated.current) {
ref.current.setAttribute('style', `left:${left}px; top:${top}px; visibility:visible`)
} else {
timeout.current = setTimeout(() => {
ref.current.setAttribute('style', `left:${left}px; top:${top}px; visibility:visible`)
activated.current = true
}, 1000);
}
} else {
ref.current.setAttribute('style', `visibility:hidden`)
activated.current = false
}
})
}, [])
return <div className="tooltip" ref={ref}>
{state}
<div className="arrow"></div>
</div>
}
function getTextWidth(text, font = "16px sans-serif") {
// re-use canvas object for better performance
let canvas = getTextWidth.canvas || (getTextWidth.canvas = document.createElement("canvas"));
let context = canvas.getContext("2d");
context.font = font;
let metrics = context.measureText(text);
return metrics.width;
}

View File

@ -4,7 +4,7 @@ const tailwindcss = require('tailwindcss')
module.exports = {
entry: {
app: './src/app.jsx',
app: './src/react/app.jsx',
scene: './src/Scene.js',
},
output: {