feat: preact-router and GraphInput node balance modifier

backup-1
Antonio De Lucreziis 2 years ago
parent 35759b6d1d
commit 160007eb0d

Binary file not shown.

@ -22,6 +22,7 @@
"@ruby/wasm-wasi": "^2.6.2", "@ruby/wasm-wasi": "^2.6.2",
"katex": "^0.16.11", "katex": "^0.16.11",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"preact-router": "^4.1.2",
"vite-plugin-wasm": "^3.3.0" "vite-plugin-wasm": "^3.3.0"
} }
} }

@ -20,18 +20,21 @@ export const View = ({}) => {
{ {
id: id1, id: id1,
label: '1', label: '1',
balance: 0,
x: 100 + Math.random() * 500, x: 100 + Math.random() * 500,
y: 100 + Math.random() * 300, y: 100 + Math.random() * 300,
}, },
{ {
id: id2, id: id2,
label: '2', label: '2',
balance: 0,
x: 100 + Math.random() * 500, x: 100 + Math.random() * 500,
y: 100 + Math.random() * 300, y: 100 + Math.random() * 300,
}, },
{ {
id: id3, id: id3,
label: '3', label: '3',
balance: 0,
x: 100 + Math.random() * 500, x: 100 + Math.random() * 500,
y: 100 + Math.random() * 300, y: 100 + Math.random() * 300,
}, },

@ -56,6 +56,7 @@ export const GraphInput = ({ graph, setGraph }) => {
{ {
id: crypto.randomUUID(9).split('-')[0], id: crypto.randomUUID(9).split('-')[0],
label: '?', label: '?',
balance: 0,
x, x,
y, y,
}, },
@ -193,7 +194,7 @@ export const GraphInput = ({ graph, setGraph }) => {
))} ))}
</div> </div>
<div class="nodes"> <div class="nodes">
{graph.nodes.map(({ id, label, x, y }, index) => ( {graph.nodes.map(({ id, label, balance, x, y }, index) => (
<div <div
class={[ class={[
'node', 'node',
@ -204,44 +205,6 @@ export const GraphInput = ({ graph, setGraph }) => {
.filter(Boolean) .filter(Boolean)
.join(' ')} .join(' ')}
style={{ '--x': x, '--y': y }} style={{ '--x': x, '--y': y }}
onMouseDown={e => {
if (interacting) return
if (e.ctrlKey) {
setInteracting({
type: 'arrow',
index,
initialPos: { x, y },
initialDragPos: { x: e.x, y: e.y },
x: x,
y: y,
target: null,
})
} else {
setInteracting({
type: 'drag',
index,
initialPos: { x, y },
initialDragPos: { x: e.x, y: e.y },
})
}
}}
onMouseMove={e => {
if (interacting?.type === 'arrow' && interacting.index !== index) {
setInteracting(i => ({ ...i, target: index }))
}
}}
onMouseLeave={e => {
if (interacting && interacting.type === 'arrow') {
setInteracting(i => ({ ...i, target: null }))
}
}}
onDblclick={e => {
setInteracting({
type: 'edit-node',
index,
})
}}
onKeyDown={e => { onKeyDown={e => {
if ( if (
(e.key === 'Enter' || e.key === 'Escape') && (e.key === 'Enter' || e.key === 'Escape') &&
@ -251,27 +214,110 @@ export const GraphInput = ({ graph, setGraph }) => {
} }
}} }}
> >
{interacting?.type === 'edit-node' && interacting.index === index ? ( <div class="popup">
<input <button
type="text" onClick={() => {
value={label}
onInput={e =>
setGraph(g => { setGraph(g => {
const newNodes = [...g.nodes] const newNodes = [...g.nodes]
newNodes[interacting.index] = { newNodes[index] = {
...g.nodes[interacting.index], ...g.nodes[index],
label: e.target.value, balance: g.nodes[index].balance + 1,
} }
return { return { ...g, nodes: newNodes }
...g, })
nodes: newNodes, }}
>
+
</button>
<button
onClick={() => {
setGraph(g => {
const newNodes = [...g.nodes]
newNodes[index] = {
...g.nodes[index],
balance: g.nodes[index].balance - 1,
} }
return { ...g, nodes: newNodes }
})
}}
>
-
</button>
</div>
<div
class="node-ball"
onMouseDown={e => {
if (interacting) return
if (e.ctrlKey) {
setInteracting({
type: 'arrow',
index,
initialPos: { x, y },
initialDragPos: { x: e.x, y: e.y },
x: x,
y: y,
target: null,
})
} else {
setInteracting({
type: 'drag',
index,
initialPos: { x, y },
initialDragPos: { x: e.x, y: e.y },
}) })
} }
/> }}
) : ( onMouseMove={e => {
label if (interacting?.type === 'arrow' && interacting.index !== index) {
setInteracting(i => ({ ...i, target: index }))
}
}}
onMouseLeave={e => {
if (interacting && interacting.type === 'arrow') {
setInteracting(i => ({ ...i, target: null }))
}
}}
onDblclick={e => {
setInteracting({
type: 'edit-node',
index,
})
}}
>
{interacting?.type === 'edit-node' && interacting.index === index ? (
<input
type="text"
value={label}
onInput={e =>
setGraph(g => {
const newNodes = [...g.nodes]
newNodes[interacting.index] = {
...g.nodes[interacting.index],
label: e.target.value,
}
return {
...g,
nodes: newNodes,
}
})
}
/>
) : (
label
)}
</div>
{balance !== 0 && (
<div class="balance">
{balance < 0 ? (
<>&minus;{Math.abs(balance)}</>
) : (
<>+{Math.abs(balance)}</>
)}
</div>
)} )}
</div> </div>
))} ))}

@ -4,6 +4,7 @@ import '@fontsource/inter/latin.css'
import _ from 'lodash' import _ from 'lodash'
import { render } from 'preact' import { render } from 'preact'
import Router, { route } from 'preact-router'
import { useState } from 'preact/hooks' import { useState } from 'preact/hooks'
const ViewRegistry = Object.fromEntries( const ViewRegistry = Object.fromEntries(
@ -25,7 +26,7 @@ const NewAlgorithmBox = ({ title, description, onClick }) => (
</div> </div>
) )
const AlgorithmChooserView = ({ setCurrentView }) => { const AlgorithmChooserView = ({}) => {
const sections = _.groupBy(ViewRegistry, 'metadata.group') const sections = _.groupBy(ViewRegistry, 'metadata.group')
return ( return (
@ -41,7 +42,7 @@ const AlgorithmChooserView = ({ setCurrentView }) => {
<NewAlgorithmBox <NewAlgorithmBox
title={metadata.title} title={metadata.title}
description={metadata.description} description={metadata.description}
onClick={() => setCurrentView(id)} onClick={() => route(id)}
/> />
))} ))}
</div> </div>
@ -98,7 +99,12 @@ const App = ({}) => {
<h2>History</h2> <h2>History</h2>
</aside> </aside>
<main> <main>
<Main /> <Router>
<AlgorithmChooserView path="/" />
{Object.entries(ViewRegistry).map(([id, { View }]) => (
<View path={id} />
))}
</Router>
</main> </main>
</> </>
) )

@ -51,6 +51,22 @@ h6 {
/* Components */ /* Components */
button {
outline: none;
border: none;
background: #555;
color: #fff;
border-radius: 0.25rem;
cursor: pointer;
&:hover {
background: #777;
}
}
.boxes { .boxes {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
@ -174,24 +190,71 @@ h6 {
transform: translate(-50%, -50%); transform: translate(-50%, -50%);
display: grid; pointer-events: all;
place-content: center;
width: 2.5rem; > .balance {
height: 2.5rem; position: absolute;
top: 100%;
left: 50%;
background: #f0f0f0; font-size: 18px;
border: 2px solid #333;
border-radius: 1.25rem;
pointer-events: all; transform: translateX(-50%);
}
cursor: move; > .node-ball {
display: grid;
place-content: center;
&.targeted { width: 2.5rem;
height: 2.5rem;
background: #f0f0f0;
border: 2px solid #333;
border-radius: 1.25rem;
cursor: move;
}
> .popup {
position: absolute;
z-index: -1;
inset: -0.25rem;
height: 4rem;
/* background: #e0e0e0dd;
border: 2px solid #333; */
/* border-radius: 0.5rem; */
display: flex;
flex-direction: column;
gap: 0.25rem;
right: -2.25rem;
top: 50%;
transform: translateY(-50%);
padding: 0.25rem;
padding-left: 3rem;
opacity: 0;
> * {
flex-grow: 1;
}
}
&.targeted > .node-ball {
color: #fff; color: #fff;
background: green; background: green;
} }
&:hover > .popup {
opacity: 1;
}
} }
} }
} }

Loading…
Cancel
Save