This repository has been archived by the owner on Dec 1, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
GeoBlock visual component #977
Merged
hoanphungt
merged 68 commits into
hoan-geoblock-list
from
hoan-geoblock-visual-component
Oct 28, 2021
Merged
Changes from all commits
Commits
Show all changes
68 commits
Select commit
Hold shift + click to select a range
b2c69ac
GeoBlockVisualFlow component with react-flow
hoanphungt d8ca185
build components
hoanphungt 5ee6a29
Create eedges in createGraphLayout
hoanphungt fba18d1
Fix number blocks with same id
hoanphungt 19bd226
Group block
hoanphungt df2e40b
Output block
hoanphungt 1fab698
merge outputElement with blockElements
hoanphungt 6531edd
Change input of input blocks
hoanphungt 82a08c7
block components
hoanphungt f632dc7
Sidebar component
hoanphungt ae1757b
Drag and drop feature for new blocks
hoanphungt 8e745de
small fix for raster block style
hoanphungt e000990
Improve GroupBlock component
hoanphungt b9296e9
JSON validator
hoanphungt e6cec93
save geoblock graph to json string
hoanphungt 582973f
Add reset button
hoanphungt b1f394f
Change variable names
hoanphungt a86e15a
Add more blocks to type definition
hoanphungt 61816a1
dynamic height for blocks based on number of inputs
hoanphungt 9bcf73f
type of FillNoData block
hoanphungt fbe23f0
improve
hoanphungt a0f5aaa
refactor helper function onBlockValueChange
hoanphungt 5a654f8
set output block dynamically
hoanphungt 546c211
Fix a bug with number block
hoanphungt 820b1d3
Check for number of output nodes in the graph
hoanphungt 39361d7
styling
hoanphungt 57c652e
hanlde output hanlders for group blocks
hoanphungt 8a0d5bc
Add more blocks to the type definition
hoanphungt 0b0fd23
Add title to handles and blocks
hoanphungt b39dbae
styling of blocks
hoanphungt ea4fda4
Edges with colors
hoanphungt 2a35b4b
Boolean block
hoanphungt e0b80d2
improve styling
hoanphungt e5fc368
Improve styling of input fields
hoanphungt 06a5fba
Get raster elements and block elements
hoanphungt 084a74d
Add more blocks to type definition
hoanphungt f6e3627
add block class to title
hoanphungt 00231e6
Remove demo components
hoanphungt c71b676
Refactor elements and setElements hooks to parent
hoanphungt a604c2e
Raster UUID validator
hoanphungt 76ed436
Output block validation
hoanphungt 82533d4
Fix title of GroupBlock
hoanphungt ff2a990
Prevent connect wrong type of block data
hoanphungt 4739a98
Not allow to connect to target handle that is already used
hoanphungt af3ad9a
Fix reset view button to rebuild the geoblock source
hoanphungt 6e86009
Remove reset view button
hoanphungt 22efe3b
Test validate button
hoanphungt 658aca7
target handle validator
hoanphungt 931639d
Validate orphan parts of the block
hoanphungt 9026317
Validation first
hoanphungt 550a034
Add string blocks
hoanphungt cac937a
Add new string block
hoanphungt 0da4152
Add title to block
hoanphungt c4a29d8
Handle array value input
hoanphungt 36c687a
Handle wrong raster input block
hoanphungt 1947f1a
Array block improvements
hoanphungt 265cfcf
improvements
hoanphungt 2f15ca3
Improve check for blocks to add to graph in convertElementsToGraph fu…
hoanphungt dc542d5
Build source
hoanphungt 6a423dc
Validation with dry-run query
hoanphungt b2f2d9a
Release 0.2.84
hoanphungt 945dfb6
Handle new geoblock case or geoblock with no value
hoanphungt 5ca56f0
Release 0.2.85
hoanphungt 1b8f0cc
reset elements to empty array when unmounted
hoanphungt 50e5efc
add comment
hoanphungt ffe2274
Add minimap for very big geoblock
hoanphungt 87fca2c
refactor geoblock validate function
hoanphungt 94b74c0
remove the MVP
hoanphungt File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
182 changes: 177 additions & 5 deletions
182
src/data_management/geoblocks/buildComponents/GeoBlockVisualComponent.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,181 @@ | ||
import React from 'react'; | ||
import React, { useRef, useState } from 'react'; | ||
import ReactFlow, { | ||
addEdge, | ||
Connection, | ||
ConnectionLineType, | ||
Controls, | ||
Edge, | ||
Elements, | ||
isNode, | ||
MiniMap, | ||
Position, | ||
ReactFlowProvider, | ||
removeElements, | ||
updateEdge, | ||
useUpdateNodeInternals | ||
} from 'react-flow-renderer'; | ||
import { SideBar } from './blockComponents/SideBar'; | ||
import { Block } from './blockComponents/Block'; | ||
import { BooleanBlock } from './blockComponents/BooleanBlock'; | ||
import { GroupBlock } from './blockComponents/GroupBlock'; | ||
import { NumberBlock } from './blockComponents/NumberBlock'; | ||
import { RasterBlock } from './blockComponents/RasterBlock'; | ||
import { StringBlock } from './blockComponents/StringBlock'; | ||
import { ArrayBlock } from './blockComponents/ArrayBlock'; | ||
import { geoblockType } from '../../../types/geoBlockType'; | ||
import { getBlockData } from '../../../utils/geoblockUtils'; | ||
import { targetHandleValidator } from '../../../utils/geoblockValidators'; | ||
import edgeStyle from './blockComponents/Edge.module.css'; | ||
|
||
interface MyProps { | ||
elements: Elements, | ||
setElements: React.Dispatch<React.SetStateAction<Elements<any>>> | ||
} | ||
|
||
const GeoBlockVisualFlow = (props: MyProps) => { | ||
const { elements, setElements } = props; | ||
const reactFlowWrapper = useRef<any>(null); | ||
const updateNodeInternals = useUpdateNodeInternals(); | ||
const [reactFlowInstance, setReactFlowInstance] = useState<any>(null); | ||
|
||
// gets called after end of edge gets dragged to another source or target | ||
const onEdgeUpdate = (oldEdge: Edge, newConnection: Connection) => { | ||
setElements((els) => updateEdge(oldEdge, newConnection, els)); | ||
newConnection.target && updateNodeInternals(newConnection.target); // update node internals | ||
}; | ||
|
||
const onConnect = (params: Edge | Connection) => { | ||
const connectError = targetHandleValidator(elements, params); | ||
if (connectError) return console.error(connectError.errorMessage); | ||
|
||
setElements((els) => { | ||
const source = els.find(el => el.id === params.source)!; | ||
return addEdge({ | ||
...params, | ||
type: ConnectionLineType.SmoothStep, | ||
animated: true, | ||
className: ( | ||
source.type === 'NumberBlock' ? edgeStyle.NumberEdge : | ||
source.type === 'StringBlock' ? edgeStyle.StringEdge : | ||
source.type === 'BooleanBlock' ? edgeStyle.BooleanEdge : | ||
edgeStyle.BlockEdge | ||
) | ||
}, els); | ||
}); | ||
params.target && updateNodeInternals(params.target); // update node internals | ||
}; | ||
|
||
const onElementsRemove = (elementsToRemove: Elements) => { | ||
setElements((els) => removeElements(elementsToRemove, els)) | ||
}; | ||
|
||
const onLoad = (_reactFlowInstance: any) => { | ||
setReactFlowInstance(_reactFlowInstance); | ||
}; | ||
|
||
// Drag and drop actions | ||
const onDragOver = (event: React.DragEvent<HTMLDivElement>) => { | ||
event.preventDefault(); | ||
event.dataTransfer.dropEffect = 'move'; | ||
}; | ||
|
||
const onDrop = (event: React.DragEvent<HTMLDivElement>) => { | ||
event.preventDefault(); | ||
if (reactFlowWrapper && reactFlowWrapper.current) { | ||
const reactFlowBounds = reactFlowWrapper.current.getBoundingClientRect(); | ||
const blockName = event.dataTransfer.getData('application/reactflow'); | ||
const position = reactFlowInstance.project({ | ||
x: event.clientX - reactFlowBounds.left, | ||
y: event.clientY - reactFlowBounds.top, | ||
}); | ||
const sourcePosition = Position.Right; | ||
const targetPosition = Position.Left; | ||
|
||
// Keep track of number of block elements in the graph to create block id | ||
const numberOfBlocks = elements.filter(elm => { | ||
// @ts-ignore | ||
return isNode(elm) && elm.data && elm.data.classOfBlock === geoblockType[blockName].class; | ||
}).length; | ||
const idOfNewBlock = blockName + numberOfBlocks; | ||
|
||
const newBlock = { | ||
id: idOfNewBlock, | ||
type: ( | ||
blockName === 'RasterBlock' || blockName === 'NumberBlock' || blockName === 'BooleanBlock' || blockName === 'StringBlock' ? blockName : | ||
blockName === 'Group' || blockName === 'FillNoData' ? 'GroupBlock' : 'Block' | ||
), | ||
position, | ||
sourcePosition, | ||
targetPosition, | ||
data: getBlockData(blockName, numberOfBlocks, idOfNewBlock, setElements) | ||
}; | ||
console.log('newBlock', newBlock); | ||
setElements((es) => es.concat(newBlock)); | ||
}; | ||
}; | ||
|
||
export const GeoBlockVisualComponent = () => { | ||
return ( | ||
<div> | ||
GeoBlock Visual Component | ||
<div | ||
style={{ | ||
display: 'grid', | ||
gridTemplateColumns: '1fr 150px', | ||
columnGap: 10, | ||
margin: '20px 0', | ||
height: 550 | ||
}} | ||
> | ||
<ReactFlow | ||
ref={reactFlowWrapper} | ||
elements={elements} | ||
onElementsRemove={onElementsRemove} | ||
style={{ | ||
position: 'relative', | ||
border: '1px solid lightgrey', | ||
borderRadius: 10 | ||
}} | ||
snapToGrid | ||
onEdgeUpdate={onEdgeUpdate} | ||
onConnect={onConnect} | ||
onLoad={onLoad} | ||
onDragOver={onDragOver} | ||
onDrop={onDrop} | ||
nodeTypes={{ | ||
Block: Block, | ||
BooleanBlock: BooleanBlock, | ||
GroupBlock: GroupBlock, | ||
RasterBlock: RasterBlock, | ||
NumberBlock: NumberBlock, | ||
StringBlock: StringBlock, | ||
ArrayBlock: ArrayBlock | ||
}} | ||
> | ||
{elements.length > 100 ? ( | ||
<MiniMap | ||
nodeColor={(node) => { | ||
if (node.type === 'RasterBlock') return 'orange'; | ||
if (node.type === 'Block' && node.data.outputBlock) return 'red'; | ||
if (node.type === 'Block' || node.type === 'GroupBlock') return 'green'; | ||
return 'lightgrey'; | ||
}} | ||
nodeStrokeWidth={3} | ||
/> | ||
) : null} | ||
<Controls /> | ||
</ReactFlow> | ||
<SideBar /> | ||
</div> | ||
) | ||
} | ||
} | ||
|
||
export const GeoBlockVisualComponent = (props: MyProps) => ( | ||
// Wrap the GeoBlockVisualFlow component inside the ReactFlowProvider | ||
// to have access to the useUpdateNodeInternals hook of react-flow. | ||
// the useUpdateNodeInternals hook is used to update the node manually | ||
// when there are changes that cannot be automatically updated to the node. | ||
<ReactFlowProvider> | ||
<GeoBlockVisualFlow | ||
elements={props.elements} | ||
setElements={props.setElements} | ||
/> | ||
</ReactFlowProvider> | ||
) |
33 changes: 33 additions & 0 deletions
33
src/data_management/geoblocks/buildComponents/blockComponents/ArrayBlock.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
import React from 'react'; | ||
import { Handle, Node, Position } from 'react-flow-renderer'; | ||
import formStyles from './../../../../styles/Forms.module.css'; | ||
// import styles from './Block.module.css'; | ||
|
||
interface ArrayBlockInput { | ||
value: string, | ||
onChange: (value: string) => void, | ||
} | ||
|
||
export const ArrayBlock = (props: Node<ArrayBlockInput>) => { | ||
const { value } = props.data!; | ||
return ( | ||
<div> | ||
<input | ||
type="text" | ||
title={JSON.stringify(value)} | ||
className={formStyles.FormControl} | ||
// onChange={e => onChange(e.target.value)} | ||
value={JSON.stringify(value)} | ||
readOnly | ||
style={{ | ||
width: 140, | ||
fontSize: 12 | ||
}} | ||
/> | ||
<Handle | ||
type="source" | ||
position={Position.Right} | ||
/> | ||
</div> | ||
) | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If this id always needs to be unique, then maybe use a library to generate a uuid instead.
I used "nanoid" before.
What for example happens if a user deletes nodes? Now for example node 10 might still be in the graph, but the amount of nodes might be 5.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, this id needs to be unique. What you suggested is good. I will look into it. Thanks