react-flow-chart
react-flow-chart copied to clipboard
any chance of a short tutorial
Hi, This is really cool, i've managed to get something together, however I have had to sort of 'hack' the stories to get something, as the examples directly reference the files in the project, rather than as a seperate project. I realise this is probably my misunderstanding of react but it would be great to have some examples of using this from a seperate projects perspective. Thanks again!
EDIT (more detail):
As an example, I am trying to combine some of the examples in the stories. I've got this far (and this does work), however I've had to copy a lot of the code out of your library to get it working. Note I'm not using typescript (tsx) I guess if I use your library correctly, It doesn't matter and will all be handled for me, but when I copy some code out and I'm not using typescript it doesn't work. The below is supposed to have a sidebar that I can drag and drop from, and another sidebar (desired to be along the bottom but thats fine for now) showing details of a selected node. However it doesn't show the selected node's details, and I'm not sure why exactly. My goal is to have the types of components to be loaded from a json object into the list of available nodes, and then be able to drag them onto the diagram. I'd then like to know how to export the underlying chart as json for a sort of "save" feature but thats easier once I have the state successfully holding the state of the chart and once the node types come from a common source (json file)
Any help to this goal would be appreciated - its a super cool library, I've been looking for something nice like this and this is the best I found yet.
import React, {Component} from 'react'
import { cloneDeep, mapValues } from 'lodash'
import {FlowChart, FlowChartWithState} from "@mrblenny/react-flow-chart"
import { chartSimple} from "./simpleChart";
import {Page, Content, Sidebar, SidebarItem} from "./flowchartComponents"
import * as actions from "@mrblenny/react-flow-chart"
import styled, { createGlobalStyle } from 'styled-components'
const Outer = styled.div`
padding: 30px;
width: 250px;
`
const Message = styled.div`
margin: 10px;
padding: 10px;
line-height: 1.4em;
`
const Input = styled.input`
padding: 10px;
border: 1px solid cornflowerblue;
width: 100%;
`
const Circle = styled.div`
position: absolute;
width: 150px;
height: 150px;
padding: 30px;
// display: flex;
// justify-content: center;
align-items: center;
background: #d30000;
color: white;
border-radius: 25%;
opacity: 0.5;
`
const PortDefaultOuter = styled.div`
width: 24px;
height: 24px;
background: cornflowerblue;
cursor: pointer;
display: flex;
justify-content: center;
align-items: center;
`
const PortCustom = (props) => (
<PortDefaultOuter>
{ props.port.properties && props.port.properties.value === 'yes' && (
<svg style={{ width: '24px', height: '24px' }} viewBox="0 0 24 24">
<path fill="white" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z" />
</svg>
)}
{ props.port.properties && props.port.properties.value === 'no' && (
<svg style={{ width: '24px', height: '24px' }} viewBox="0 0 24 24">
<path fill="white" d="M19,6.41L17.59,5L12,10.59L6.41,5L5,6.41L10.59,12L5,17.59L6.41,19L12,13.41L17.59,19L19,17.59L13.41,12L19,6.41Z" />
</svg>
)}
{ !props.port.properties && (
<svg style={{ width: '24px', height: '24px' }} viewBox="0 0 24 24">
<path fill="white" d="M7.41,8.58L12,13.17L16.59,8.58L18,10L12,16L6,10L7.41,8.58Z" />
</svg>
)}
</PortDefaultOuter>
)
const NodeInnerCustom = ({ node, config }) => {
switch (node.type) {
case 'output-only':
return (
<Outer>
<p>Use Node inner to customise the content of the node</p>
</Outer>
)
case 'type1':
return (
<Outer>
<p>Lorem Ipsum type1</p>
<br />
<Input
type="string"
placeholder="path"
onChange={(e) => console.log(e)}
onClick={(e) => e.stopPropagation()}
onMouseUp={(e) => e.stopPropagation()}
onMouseDown={(e) => e.stopPropagation()}
/>
</Outer>
)
case 'type2':
return (
<Outer>
<p>Lorem Ipsum type2</p>
<Input
type="number"
placeholder="level"
onChange={(e) => console.log(e)}
onClick={(e) => e.stopPropagation()}
onMouseUp={(e) => e.stopPropagation()}
onMouseDown={(e) => e.stopPropagation()}
/>
</Outer>
)
case 'type3':
return (
<Outer>
<p>Lorem Ipsum type3</p>
<br />
<Input
type="string"
placeholder="path to page"
onChange={(e) => console.log(e)}
onClick={(e) => e.stopPropagation()}
onMouseUp={(e) => e.stopPropagation()}
onMouseDown={(e) => e.stopPropagation()}
/>
</Outer>
)
case 'type4':
return (
<Outer>
<p>Lorem Ipsum type4</p>
<br />
<Input
type="string"
placeholder="email address"
onChange={(e) => console.log(e)}
onClick={(e) => e.stopPropagation()}
onMouseUp={(e) => e.stopPropagation()}
onMouseDown={(e) => e.stopPropagation()}
/>
<Input
type="string"
placeholder="content"
onChange={(e) => console.log(e)}
onClick={(e) => e.stopPropagation()}
onMouseUp={(e) => e.stopPropagation()}
onMouseDown={(e) => e.stopPropagation()}
/>
</Outer>
)
case 'type5':
return (
<Outer>
<p>Lorem Ipsum type 5</p>
<br />
<Input
type="string"
placeholder="table name"
onChange={(e) => console.log(e)}
onClick={(e) => e.stopPropagation()}
onMouseUp={(e) => e.stopPropagation()}
onMouseDown={(e) => e.stopPropagation()}
/>
</Outer>
)
case 'type6':
return (
<Outer>
<p>Lorem Ipsum type6</p>
<br />
</Outer>
)
default:
return (
<Outer>
<p>Lorem Ipsum Default</p>
<br />
<Input
type="number"
placeholder="Some Input"
onChange={(e) => console.log(e)}
onClick={(e) => e.stopPropagation()}
onMouseUp={(e) => e.stopPropagation()}
onMouseDown={(e) => e.stopPropagation()}
/>
</Outer>
)
}
}
class DragAndDropSidebar extends Component {
constructor(props) {
super(props)
this.state = cloneDeep(chartSimple)
}
render = () => {
const chart = this.state
const stateActions = mapValues(actions, (any) =>
(...args) => this.setState(...args))
return (
<Page>
<Content>
<FlowChartWithState initialValue={chart} config={{
snapToGrid: true,
}} Components={{
NodeInner: NodeInnerCustom,
}} callbacks={stateActions}/>
</Content>
<Sidebar>
{ chart.selected.type
? <Message>
<div>Type: {chart.selected.type}</div>
<div>ID: {chart.selected.id}</div>
<br/>
{/*
We can re-use the onDeleteKey action. This will delete whatever is selected.
Otherwise, we have access to the state here so we can do whatever we want.
*/}
<Button onClick={() => stateActions.onDeleteKey({})}>Delete</Button>
</Message>
: <Message>Click on a Node, Port or Link</Message> }
</Sidebar>
<Sidebar>
<SidebarItem
type="type1"
ports={{
port1: {
id: 'port1',
type: 'right',
properties: {
custom: 'property',
value: "yes",
},
},
}}
properties={{
custom: 'property',
}}
/>
<SidebarItem
type="type2"
ports={{
port1: {
id: 'port1',
type: 'right',
properties: {
custom: 'property',
value: "yes",
},
},
}}
/>
<SidebarItem
type="type3"
ports={{
port1: {
id: 'port1',
type: 'left',
properties: {
custom: 'property',
value: "no",
},
},
}}
/>
<SidebarItem
type="type4"
ports={{
port1: {
id: 'port1',
type: 'left',
properties: {
custom: 'property',
value: "no",
},
},
}}
/>
<SidebarItem
type="type5"
ports={{
port1: {
id: 'port1',
type: 'left',
properties: {
custom: 'property',
value: "no",
},
},
}}
/>
<SidebarItem
type="type6"
ports={{
port1: {
id: 'port1',
type: 'left',
properties: {
custom: 'property',
value: "no",
},
},
port2: {
id: 'port2',
type: 'right',
properties: {
custom: 'property',
value: "yes",
},
},
}}
/>
</Sidebar>
</Page>
)
}
}
export default DragAndDropSidebar