Skip to content
This repository has been archived by the owner on Jul 15, 2024. It is now read-only.

Commit

Permalink
Add ability to hide and reveal contracts (#27)
Browse files Browse the repository at this point in the history
  • Loading branch information
sdlyy authored Feb 9, 2024
1 parent 2119fa3 commit 9be443a
Show file tree
Hide file tree
Showing 6 changed files with 98 additions and 23 deletions.
15 changes: 14 additions & 1 deletion packages/frontend/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,10 @@ import { Viewport } from './view/Viewport'
export function App() {
// load the initial nodes from the store that gets rehydrated from local storage at startup
const initialNodes = useStore((state) => state.nodes)
const hiddenNodesIds = useStore((state) => state.hiddenNodesIds)
const projectId = useStore((state) => state.projectId)
const updateNodeLocations = useStore((state) => state.updateNodeLocations)
const setHiddenNodes = useStore((state) => state.setHiddenNodes)
const setProjectId = useStore((state) => state.setProjectId)
const [nodes, setNodes] = useState<SimpleNode[]>(
initialNodes.map(nodeToSimpleNode),
Expand All @@ -48,6 +50,7 @@ export function App() {

function clear() {
setNodes([])
setHiddenNodes(() => [])
}

function save() {
Expand All @@ -74,6 +77,10 @@ export function App() {
setNodes((nodes) => merge(nodes, result))
}

function revealAllNodes() {
setHiddenNodes(() => [])
}

async function loadFromFile(file: File) {
console.log('LOADING')

Expand Down Expand Up @@ -164,8 +171,14 @@ export function App() {
</button>
</div>

<div className="flex items-center">
<div className="flex items-center gap-1">
<AutoLayoutButton />
<button
onClick={revealAllNodes}
className="rounded bg-blue-400 px-4 py-2 font-bold text-white hover:bg-blue-700"
>
Reveal all ({hiddenNodesIds.length})
</button>
<button
className="px-1 text-2xl hover:bg-gray-300"
type="button"
Expand Down
1 change: 1 addition & 0 deletions packages/frontend/src/store/State.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { SimpleNode } from '../api/SimpleNode'

export interface State {
readonly selectedNodeIds: readonly string[]
readonly hiddenNodesIds: readonly string[]
readonly nodes: readonly Node[]
readonly selection?: Box
readonly transform: {
Expand Down
2 changes: 2 additions & 0 deletions packages/frontend/src/store/actions/Actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,6 @@ export interface Actions {
updateNodes: (nodes: SimpleNode[]) => void
updateNodeLocations: (locations: NodeLocations) => void
setProjectId: (projectId: string) => void

setHiddenNodes: (update: (currentlyHiddenIds: string[]) => string[]) => void
}
11 changes: 11 additions & 0 deletions packages/frontend/src/store/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export const useStore = create<State & Actions>()(
persist(
(set) => ({
selectedNodeIds: [],
hiddenNodesIds: [],
nodes: [],
selection: undefined,
transform: { offsetX: 0, offsetY: 0, scale: 1 },
Expand Down Expand Up @@ -43,12 +44,22 @@ export const useStore = create<State & Actions>()(
set((state) => updateNodeLocations(state, ...args)),
setProjectId: (projectId: string) =>
set((state) => ({ ...state, projectId: projectId })),

setHiddenNodes: (updateFn) => {
set((state) => {
// stale-state
const hiddenNodesIds = updateFn([...state.hiddenNodesIds])

return { ...state, hiddenNodesIds }
})
},
}),
{
name: 'store',
partialize: (state) => {
return {
nodes: state.nodes,
hiddenNodesIds: state.hiddenNodesIds,
projectId: state.projectId,
}
},
Expand Down
39 changes: 34 additions & 5 deletions packages/frontend/src/view/NodeView.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import classNames from 'classnames'
import { useCallback, useRef } from 'react'
import { useCallback, useEffect, useRef } from 'react'

import { Node } from '../store/State'
import { useStore } from '../store/store'
Expand All @@ -11,13 +11,37 @@ export interface NodeViewProps {
selected: boolean
discovered: boolean
onDiscover: (nodeId: string) => void
onHideNode: (nodeId: string) => void
loading: boolean
}

export function NodeView(props: NodeViewProps) {
const ref = useRef<HTMLDivElement>(null)

const updateNodeLocations = useStore((state) => state.updateNodeLocations)
// Using ref instead of inline event listener
// to prevent side-menu from flashing on hidden node
const hideRef = useRef<HTMLButtonElement>(null)
const onHide = useCallback(
(e: MouseEvent) => {
e.preventDefault()
e.stopPropagation()
props.onHideNode(props.node.simpleNode.id)
},
[props.onHideNode, props.node.simpleNode.id],
)

useEffect(() => {
if (hideRef.current) {
// The `true` argument here signifies that this listener is for the capture phase
// actively prevent store-attached events from firing
hideRef.current.addEventListener('mousedown', onHide, true)
}

return () => {
hideRef.current?.removeEventListener('mousedown', onHide, true)
}
}, [])

const onDiscover = useCallback(() => {
props.onDiscover(props.node.simpleNode.id)
Expand Down Expand Up @@ -57,11 +81,16 @@ export function NodeView(props: NodeViewProps) {
)}
>
<div className="truncate">{props.node.simpleNode.name}</div>
{!props.discovered && (
<button onClick={onDiscover} disabled={props.loading}>
{props.loading ? '🔄' : '🔍'}
<div className="flex items-center justify-center gap-2">
{!props.discovered && (
<button onClick={onDiscover} disabled={props.loading}>
{props.loading ? '🔄' : '🔍'}
</button>
)}
<button ref={hideRef} disabled={props.loading}>
👁️
</button>
)}
</div>
</div>
{props.node.fields.map(({ name, connection }, i) => (
<div className="relative" key={i}>
Expand Down
53 changes: 36 additions & 17 deletions packages/frontend/src/view/Viewport.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,41 +21,60 @@ export function Viewport(props: ViewportProps) {
updateNodes(props.nodes)
}, [updateNodes, props.nodes])

const setHiddenNodes = useStore((state) => state.setHiddenNodes)

const nodes = useStore((state) => state.nodes)
const selectedNodeIds = useStore((state) => state.selectedNodeIds)
const hiddenNodesIds = useStore((state) => state.hiddenNodesIds)

const transform = useStore((state) => state.transform)
const mouseSelection = useStore((state) => state.mouseSelection)

const visibleNodes = nodes.filter(
(node) => !hiddenNodesIds.includes(node.simpleNode.id),
)

function hideNode(nodeId: string) {
setHiddenNodes((nodes) => [...nodes, nodeId])
}

return (
<div
ref={containerRef}
className="relative h-full w-full overflow-hidden rounded-lg bg-white"
>
<ScalableView ref={viewRef} transform={transform}>
{nodes.map((node) =>
node.fields.map(
(field, i) =>
field.connection && (
<Connection
key={`${node.simpleNode.id}-${i}-${field.connection.nodeId}`}
from={field.connection.from}
to={field.connection.to}
// Highlight selected node both ways
isHighlighted={
selectedNodeIds.includes(node.simpleNode.id) ||
selectedNodeIds.includes(field.connection.nodeId)
}
/>
),
),
{visibleNodes.map((node) =>
node.fields.map((field, i) => {
const shouldHide =
!field.connection ||
hiddenNodesIds.find((id) => id === field.connection?.nodeId)

if (shouldHide) {
return null
}

return (
<Connection
key={`${node.simpleNode.id}-${i}-${field.connection.nodeId}`}
from={field.connection.from}
to={field.connection.to}
isHighlighted={
selectedNodeIds.includes(node.simpleNode.id) ||
selectedNodeIds.includes(field.connection.nodeId)
}
/>
)
}),
)}
{nodes.map((node) => (
{visibleNodes.map((node) => (
<NodeView
key={node.simpleNode.id}
node={node}
selected={selectedNodeIds.includes(node.simpleNode.id)}
discovered={node.simpleNode.discovered}
onDiscover={props.onDiscover}
onHideNode={hideNode}
loading={!!props.loading[node.simpleNode.id]}
/>
))}
Expand Down

0 comments on commit 9be443a

Please sign in to comment.