@@ -6,7 +6,6 @@
|
||||
"release": true
|
||||
},
|
||||
"hooks": {
|
||||
"before:init": ["npm test"],
|
||||
"after:bump": "npm run build",
|
||||
"after:release": "echo Successfully released ${name} v${version} to ${repo.repository}."
|
||||
}
|
||||
|
||||
@@ -1,55 +1,20 @@
|
||||

|
||||
|
||||
## ✨ React Flow has a [new website](https://reactflow.dev) ✨
|
||||
|
||||
# React Flow
|
||||
[](https://reactflow.dev)
|
||||
|
||||
React Flow is a library for building node-based graphs. You can easily implement custom node types and it comes with components like a mini-map and graph controls. Feel free to check out the [examples](https://reactflow.dev/) or read the [blog post](https://webkid.io/blog/react-flow-node-based-graph-library/) to get started.
|
||||
|
||||
- [Key Features](#key-features)
|
||||
- [Installation](#installation)
|
||||
- [Usage](#usage)
|
||||
- [ReactFlow Component Prop Types](#react-flow-component-prop-types)
|
||||
- [Nodes](#nodes)
|
||||
- [Options](#options-1)
|
||||
- [Node Types & Custom Nodes](#node-types--custom-nodes)
|
||||
- [Handle Component](#handle-component)
|
||||
- [Edges](#edges)
|
||||
- [Options](#options)
|
||||
- [Edge Types & Custom Edges](#edge-types--custom-edges)
|
||||
- [Edge Utils](#edge-utils)
|
||||
- [Components](#components)
|
||||
- [Background](#background)
|
||||
- [Minimap](#minimap)
|
||||
- [Controls](#controls)
|
||||
- [ReactFlowProvider](#reactflowprovider)
|
||||
- [Styling](#styling)
|
||||
- [Helper Functions](#helper-functions)
|
||||
- [Access Internal State and Actions](#access-internal-state-and-actions)
|
||||
- [Examples](#examples)
|
||||
- [Development](#development)
|
||||
- [Testing](#testing)
|
||||
- **Website:** https://reactflow.dev
|
||||
- **Documentation:** https://reactflow.dev/docs
|
||||
- **Examples:** https://reactflow.dev/examples
|
||||
|
||||
## Key Features
|
||||
|
||||
* **Easy to use:** Seamless zooming & panning behaviour and single and multi-selections of elements
|
||||
* **Customizable:** Different [node](#node-types--custom-nodes) and [edge types](#edge-types--custom-edges) and support for custom nodes with multiple handles and custom edges
|
||||
* **Fast rendering:** Only nodes that have changed are re-rendered and only those that are in the viewport are displayed
|
||||
* **Utils:** Snap-to-grid and graph [helper functions](#helper-functions)
|
||||
* **Components:** [Background, Minimap and Controls](#components)
|
||||
* **Reliable**: Written in [Typescript](https://www.typescriptlang.org/) and tested with [cypress](https://www.cypress.io/)
|
||||
|
||||
In order to make this library as flexible as possible we don’t do any state updates besides the positions. This means that you need to pass the functions to remove an element or connect nodes by yourself. You can implement your own ones or use the helper functions that come with the library.
|
||||
|
||||
# Installation
|
||||
## Installation
|
||||
|
||||
```
|
||||
npm install react-flow-renderer
|
||||
```
|
||||
|
||||
# Usage
|
||||
## Quick Start
|
||||
|
||||
This is a very basic example of how to use React Flow. There are more advanced examples in the [example](/example/src) folder.
|
||||
This is a very basic example of how to use React Flow. You can find more advanced examples on the [website](https://reactflow.dev/examples).
|
||||
|
||||
```javascript
|
||||
import React from 'react';
|
||||
@@ -65,625 +30,11 @@ const elements = [
|
||||
const BasicFlow = () => <ReactFlow elements={elements} />;
|
||||
```
|
||||
|
||||
# React Flow Component Prop Types
|
||||
|
||||
#### Basic Props
|
||||
- `elements`: array of [nodes](#nodes) and [edges](#edges) *(required)*
|
||||
- `style`: css properties
|
||||
- `className`: additional class name
|
||||
|
||||
#### Flow View
|
||||
- `minZoom`: default: `0.5`
|
||||
- `maxZoom`: default: `2`
|
||||
- `defaultZoom`: default: `1`
|
||||
- `defaultPosition`: default: `[0, 0]`
|
||||
- `snapToGrid`: default: `false`
|
||||
- `snapGrid`: [x, y] array - default: `[15, 15]`
|
||||
- `onlyRenderVisibleNodes`: default: `true`
|
||||
- `translateExtent`: [default `[[-∞, -∞], [+∞, +∞]]`](https://github.com/d3/d3-zoom#zoom_translateExtent)
|
||||
|
||||
#### Event Handlers
|
||||
- `onElementClick(event: MouseEvent, element: Node | Edge)`: called when user clicks node or edge
|
||||
- `onElementsRemove(elements: Elements)`: called when user removes node or edge
|
||||
- `onNodeDragStart(event: MouseEvent, node: Node)`: node drag start
|
||||
- `onNodeDragStop(event: MouseEvent, node: Node)`: node drag stop
|
||||
- `onNodeMouseEnter(event: MouseEvent, node: Node)`: node mouse enter
|
||||
- `onNodeMouseMove(event: MouseEvent, node: Node)`: node mouse move
|
||||
- `onNodeMouseLeave(event: MouseEvent, node: Node)`: node mouse leave
|
||||
- `onNodeContextMenu(event: MouseEvent, node: Node)`: node context menu
|
||||
- `onConnect({ source, target })`: called when user connects two nodes
|
||||
- `onConnectStart(event: MouseEvent, { nodeId, handleType })`: called when user starts to drag connection line
|
||||
- `onConnectStop(event: MouseEvent)`: called when user stops to drag connection line
|
||||
- `onConnectEnd(event: MouseEvent)`: called after user stops or connects nodes
|
||||
- `onLoad(reactFlowInstance)`: called after flow is initialized
|
||||
- `onMove(flowTransform: FlowTransform)`: called when user is panning or zooming
|
||||
- `onMoveStart(flowTransform: FlowTransform)`: called when user starts panning or zooming
|
||||
- `onMoveEnd(flowTransform: FlowTransform)`: called when user ends panning or zooming
|
||||
- `onSelectionChange(elements: Elements)`: called when user selects one or multiple elements
|
||||
- `onSelectionDragStart(evt: MouseEvent, nodes: Node[])`: called when user starts to drag a selection
|
||||
- `onSelectionDrag(evt: MouseEvent, nodes: Node[])`: called when user drags a selection
|
||||
- `onSelectionDragStop(evt: MouseEvent, nodes: Node[])`: called when user stops to drag a selection
|
||||
- `onSelectionContextMenu(event: MouseEvent, nodes: Node[])`: called when user does a right-click on a selection
|
||||
- `onPaneClick(event: MouseEvent)`: called when user clicks directly on the canvas
|
||||
- `onPaneContextMenu(event: MouseEvent)`: called when user does a right-click on the canvas
|
||||
- `onPaneScroll(event: WheelEvent)`: called when user scrolls pane (only works when `zoomOnScroll` is set to `false)
|
||||
|
||||
#### Interaction
|
||||
- `nodesDraggable`: default: `true`. This applies to all nodes. You can also change the behavior of a specific node with the `draggable` node option
|
||||
- `nodesConnectable`: default: `true`. This applies to all nodes. You can also change the behavior of a specific node with the `connectable` node option
|
||||
- `elementsSelectable`: default: `true`. This applies to all elements. You can also change the behavior of a specific node with the `selectable` node option
|
||||
- `zoomOnScroll`: default: `true`
|
||||
- `zoomOnDoubleClick`: default: `true`
|
||||
- `selectNodesOnDrag`: default: `true`
|
||||
- `paneMoveable`: default: `true` - If set to `false`, panning and zooming is disabled
|
||||
|
||||
#### Element Customization
|
||||
- `nodeTypes`: object with [node types](#node-types--custom-nodes)
|
||||
- `edgeTypes`: object with [edge types](#edge-types--custom-edges)
|
||||
- `arrowHeadColor`: default: `#b1b1b7`
|
||||
|
||||
#### Connection Line Options
|
||||
- `connectionLineType`: connection line type = `default` (bezier), `straight`, `step`, `smoothstep`
|
||||
- `connectionLineStyle`: connection style as svg attributes
|
||||
- `connectionLineComponent`: [custom connection line component](/example/src/CustomConnectionLine/index.js)
|
||||
|
||||
#### Keys
|
||||
- `deleteKeyCode`: default: `8` (delete)
|
||||
- `selectionKeyCode`: default: `16` (shift)
|
||||
|
||||
**Typescript:** The interface of the ReactFlow Prop Types are exported as `ReactFlowProps`. You can use it in your code as follows:
|
||||
|
||||
```javascript
|
||||
import { ReactFlowProps } from 'react-flow-renderer';
|
||||
```
|
||||
|
||||
## React Flow Instance
|
||||
|
||||
You can receive a `reactFlowInstance` by using the `onLoad` callback:
|
||||
|
||||
```javascript
|
||||
import React from 'react';
|
||||
import ReactFlow from 'react-flow-renderer';
|
||||
|
||||
const onLoad = (reactFlowInstance) => {
|
||||
reactFlowInstance.fitView();
|
||||
}
|
||||
|
||||
const BasicFlow = () => <ReactFlow onLoad={onLoad} elements={[]} />;
|
||||
```
|
||||
|
||||
`reactFlowInstance` has the following functions:
|
||||
|
||||
### project
|
||||
|
||||
Transforms pixel coordinates to the internal ReactFlow coordinate system.
|
||||
This can be used when you drag nodes (from a side bar for example) and need the position on the pane.
|
||||
|
||||
`project = (position: XYPosition): XYPosition`
|
||||
|
||||
### fitView
|
||||
|
||||
Fits view port so that all nodes are inside the view port.
|
||||
|
||||
`fitView = ({ padding }): void`
|
||||
|
||||
### zoomIn
|
||||
|
||||
`zoomIn = (): void`
|
||||
|
||||
### zoomOut
|
||||
|
||||
`zoomOut = (): void`
|
||||
|
||||
### zoomTo
|
||||
|
||||
`zoomTo = (zoomLevel: number): void`
|
||||
|
||||
### getElements
|
||||
|
||||
`getElements = (): Elements`
|
||||
|
||||
### setTransform
|
||||
|
||||
Sets position and zoom of the pane.
|
||||
|
||||
`setTransform = (transform: FlowTransform): void`
|
||||
|
||||
# Nodes
|
||||
|
||||
There are three different [node types](#node-types--custom-nodes) (`default`, `input`, `output`) you can use. The node types differ in the number and types of handles. An input node has only a source handle, a default node has a source and a target and an output node has only a target handle. You create nodes by adding them to the `elements` array of the `ReactFlow` component.
|
||||
|
||||
Node example: `{ id: '1', type: 'input', data: { label: 'Node 1' }, position: { x: 250, y: 5 } }`
|
||||
|
||||
## Options
|
||||
|
||||
- `id`: string *(required)*
|
||||
- `position`: { x: number, y: number } *(required)*
|
||||
- `data`: {} *(required if you are using a standard type, otherwise depends on your implementation)*
|
||||
- `type`: 'input' | 'output' | 'default' or a custom one you implemented
|
||||
- `style`: css properties
|
||||
- `className`: additional class name
|
||||
- `targetPosition`: 'left' | 'right' | 'top' | 'bottom' handle position - default: 'top'
|
||||
- `sourcePosition`: 'left' | 'right' | 'top' | 'bottom' handle position - default: 'bottom'
|
||||
- `isHidden`: if `true`, the node will not be rendered
|
||||
- `draggable`: boolean - if option is not set, the node is draggable (overwrites general `nodesDraggable` option)
|
||||
- `connectable`: boolean - if option is not set, the node is connectable (overwrites general `nodesConnectable` option)
|
||||
- `selectable`: boolean - if option is not set, the node is selectable (overwrites general `elementsSelectable` option)
|
||||
|
||||
## Node Types & Custom Nodes
|
||||
|
||||
The standard node types are `input`, `default` and `output`. The default node types object looks like this:
|
||||
|
||||
```javascript
|
||||
{
|
||||
input: InputNode,
|
||||
default: DefaultNode,
|
||||
output: OutputNode
|
||||
}
|
||||
```
|
||||
|
||||
The keys represent the type names and the values are the components that get rendered.
|
||||
If you want to introduce a new type you can pass a `nodeTypes` object to the `ReactFlow` component:
|
||||
|
||||
```javascript
|
||||
nodeTypes={{
|
||||
special: MyCustomNode
|
||||
}}
|
||||
```
|
||||
|
||||
You can now use the type `special` for a node.
|
||||
The `default`, `input` and `output` types would be still available except you overwrote one of them.
|
||||
There is an example of a custom node implementation in the [custom node example](/example/src/CustomNode).
|
||||
|
||||
## Custom Node Props
|
||||
|
||||
Your custom nodes are wrapped so that the basic functions like dragging or selecting work. Custom nodes receive the following props:
|
||||
|
||||
- `id`: string
|
||||
- `data`: object
|
||||
- `type`: string
|
||||
- `selected`: boolean
|
||||
- `sourcePosition`: string
|
||||
- `targetPosition`: string
|
||||
|
||||
### Prevent dragging
|
||||
|
||||
If you have controls or other elements inside your custom node that should not drag the node you can add the class name `nodrag`.
|
||||
|
||||
## Handle Component
|
||||
|
||||
We export a `Handle` component as a helper for your custom nodes:
|
||||
|
||||
```javascript
|
||||
import { Handle } from 'react-flow-renderer';
|
||||
|
||||
const targetHandleWithValidation = (
|
||||
<Handle
|
||||
type="target"
|
||||
position="left"
|
||||
isValidConnection={(connection) => connection.source === 'some-id'}
|
||||
onConnect={params => console.log('handle onConnect', params)}
|
||||
style={{ background: '#fff' }}
|
||||
/>
|
||||
);
|
||||
```
|
||||
|
||||
### Prop Types
|
||||
|
||||
- `type`: 'source' or 'target'
|
||||
- `id`: string - you only need this when you have multiple source or target handles (otherwise the node id is used)
|
||||
- `position`: 'left', 'right', 'top' or 'bottom' handle position - default: 'top' for type target, 'bottom' for type source
|
||||
- `onConnect`: function that gets triggered on connect
|
||||
- `isValidConnection`: function receives a connection `{ target: 'some-id', source: 'another-id' }` as param, returns a boolean - default: `true`
|
||||
- `style`: css properties
|
||||
- `className`: additional class name
|
||||
|
||||
**Typescript:** The interface of the Handle Prop Types are exported as `HandleProps`.
|
||||
|
||||
### Validation
|
||||
|
||||
The handle receives the additional class names `connecting` when the connection line is above the handle and `valid` if the connection is valid. You can find an example which uses these classes [here](/example/src/Validation/index.js).
|
||||
|
||||
### Multiple Handles
|
||||
|
||||
If you need multiple source or target handles you can achieve this by creating a custom node. Normally you just use the id of a node for the `source` or `target` of an edge. If you have multiple source or target handles you need to pass an id to these handles. These ids get then added to the node id, so that you can connect a specific handle. If you have a node with an id = `1` and a handle with an id = `a` you can connect this handle by using the id = `1__a`.
|
||||
You can find an example of how to implement a custom node with multiple handles in the [custom node example](/example/src/CustomNode/ColorSelectorNode.js#L18-L29).
|
||||
|
||||
# Edges
|
||||
|
||||
React Flow comes with four [edge types](#edge-types--custom-edges) (`default`, `straight`, `step`, `smoothstep`). As the names indicate, the edges differ in the representation. The default type is a bezier edge. You create edges by adding them to your `elements` array of the `ReactFlow` component.
|
||||
|
||||
Edge example: `{ id: 'e1-2', type: 'straight', source: '1', target: '2', animated: true, label: 'edge label' }`
|
||||
|
||||
If you wanted to display this edge, you would need a node with id = 1 (source node) and another one with id = 2 (target node).
|
||||
|
||||
## Options
|
||||
|
||||
- `id`: string *(required)*
|
||||
- `source`: string (an id of a node) *(required)*
|
||||
- `target`: string (an id of a node) *(required)*
|
||||
- `type`: 'default' (bezier), 'straight', 'step' and 'smoothedge' or a custom one depending on your implementation
|
||||
- `animated`: boolean
|
||||
- `style`: css properties for the edge line path
|
||||
- `className`: additional class name
|
||||
- `label`: string
|
||||
- `labelStyle`: css properties for the text
|
||||
- `labelShowBg`: boolean - default: `true`
|
||||
- `labelBgStyle`: css properties for the text background
|
||||
- `labelBgPadding`: [number, number] background rectangle padding - default: `[2, 4]`
|
||||
- `labelBgBorderRadius`: number - default 2
|
||||
- `arrowHeadType`: 'arrow' or 'arrowclosed' - defines the arrowhead of the edge
|
||||
- `markerEndId`: custom marker end url - if this is used `arrowHeadType` gets ignored
|
||||
- `isHidden`: if `true`, the edge will not be rendered
|
||||
- `data`: {} you can use this to pass data to your custom edges.
|
||||
|
||||
You can find an example with different edges in the [edges example](https://reactflow.dev/examples/edges).
|
||||
|
||||
## Edge Types & Custom Edges
|
||||
|
||||
The basic edge types are `default` (bezier), `straight`, `step` and `smoothstep`. The default `edgeTypes` object looks like this:
|
||||
|
||||
```javascript
|
||||
{
|
||||
default: BezierEdge,
|
||||
straight: StraightEdge,
|
||||
step: StepEdge,
|
||||
smoothstep: SmoothStepEdge
|
||||
}
|
||||
```
|
||||
|
||||
The keys represent the type names and the values are the edge components.
|
||||
If you want to introduce a new edge type you can pass an `edgeTypes` object to the `ReactFlow` component:
|
||||
|
||||
```javascript
|
||||
edgeTypes={{
|
||||
special: MyCustomEdge
|
||||
}}
|
||||
```
|
||||
|
||||
Now you could use the new type `special` for an edge.
|
||||
The `straight`, `default` and `step` types would still be available unless you overwrote one of them.
|
||||
There is an implementation of a custom edge in the [edges example](/example/src/Edges/index.js).
|
||||
|
||||
## Edge Utils
|
||||
|
||||
There are several utils that help you to create a custom edge. They are used in the [custom edge](/example/src/Edges/CustomEdge.js) example.
|
||||
|
||||
### getBezierPath
|
||||
|
||||
Returns the path of a bezier edge.
|
||||
|
||||
`getBezierPath({
|
||||
sourceX,
|
||||
sourceY,
|
||||
sourcePosition = Position.Bottom,
|
||||
targetX,
|
||||
targetY,
|
||||
targetPosition = Position.Top,
|
||||
centerX, // optional
|
||||
centerY // optional
|
||||
}: GetBezierPathParams): string`
|
||||
|
||||
### getSmoothStepPath
|
||||
|
||||
Returns the path of a smooth step edge. You can set `borderRadius` = `0` to get a step edge path.
|
||||
|
||||
`getSmoothStepPath({
|
||||
sourceX,
|
||||
sourceY,
|
||||
sourcePosition = Position.Bottom,
|
||||
targetX,
|
||||
targetY,
|
||||
targetPosition = Position.Top,
|
||||
borderRadius = 5,
|
||||
centerX, // optional
|
||||
centerY // optional
|
||||
}: GetSmoothStepPathParams): string`
|
||||
|
||||
### getEdgeCenter
|
||||
|
||||
Returns the center poostion `[centerX, centerY, offsetX, offsetY]` of the edge.
|
||||
|
||||
`getEdgeCenter({ sourceX, sourceY, targetX, targetY }: GetCenterParams): [number, number, number, number]`
|
||||
|
||||
### getMarkerEnd
|
||||
|
||||
Returns the marker end url for displaying the arrow head.
|
||||
|
||||
`getMarkerEnd(arrowHeadType?: ArrowHeadType, markerEndId?: string): string`
|
||||
|
||||
# Components
|
||||
|
||||
## Background
|
||||
|
||||
React Flow comes with two background variants: **dots** and **lines**. You can use it by passing it as a children to the `ReactFlow` component:
|
||||
|
||||
```javascript
|
||||
import ReactFlow, { Background } from 'react-flow-renderer';
|
||||
|
||||
const FlowWithBackground = () => (
|
||||
<ReactFlow elements={elements}>
|
||||
<Background
|
||||
variant="dots"
|
||||
gap={12}
|
||||
size={4}
|
||||
/>
|
||||
</ReactFlow>
|
||||
);
|
||||
```
|
||||
|
||||
### Prop Types
|
||||
|
||||
- `variant`: string - has to be 'dots' or 'lines' - default: `dots`
|
||||
- `gap`: number - the gap between the dots or lines - default: `15`
|
||||
- `size`: number - the radius of the dots or the stroke width of the lines - default: `0.5`
|
||||
- `color`: string - the color of the dots or lines - default: `#81818a` for dots, `#eee` for lines
|
||||
- `style`: css properties
|
||||
- `className`: additional class name
|
||||
|
||||
**Typescript:** The interface of the Background Prop Types are exported as `BackgroundProps`.
|
||||
|
||||
## MiniMap
|
||||
|
||||
You can use the mini map plugin by passing it as a children to the `ReactFlow` component:
|
||||
|
||||
```javascript
|
||||
import ReactFlow, { MiniMap } from 'react-flow-renderer';
|
||||
|
||||
const FlowWithMiniMap = () => (
|
||||
<ReactFlow elements={elements}>
|
||||
<MiniMap
|
||||
nodeColor={(node) => {
|
||||
switch (node.type) {
|
||||
case 'input': return 'red';
|
||||
case 'default': return '#00ff00';
|
||||
case 'output': return 'rgb(0,0,255)';
|
||||
default: return '#eee';
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</ReactFlow>
|
||||
);
|
||||
```
|
||||
|
||||
### Prop Types
|
||||
|
||||
- `nodeColor`: string or function - If you pass a color as a string all nodes will get that color. If you pass a function you can return a color depending on the passed node.
|
||||
- `nodeStrokeColor`: string or function - If you pass a color as a string all nodes will get that stroke color. If you pass a function you can return a color depending on the passed node.
|
||||
- `nodeBorderRadius`: number
|
||||
- `nodeClassName`: string or function for adding an additional class to the nodes inside the mini map
|
||||
- `maskColor`: string
|
||||
- `style`: css properties
|
||||
- `className`: additional class name
|
||||
|
||||
**Typescript:** The interface of the MiniMap Prop Types are exported as `MiniMapProps`.
|
||||
|
||||
## Controls
|
||||
|
||||
The control panel contains a zoom-in, zoom-out, fit-view and a lock/unlock button. You can use it by passing it as a children to the `ReactFlow` component:
|
||||
|
||||
```javascript
|
||||
import ReactFlow, { Controls } from 'react-flow-renderer';
|
||||
|
||||
const FlowWithControls = () => (
|
||||
<ReactFlow elements={elements}>
|
||||
<Controls />
|
||||
</ReactFlow>
|
||||
);
|
||||
```
|
||||
|
||||
### Prop Types
|
||||
|
||||
- `showZoom`: boolean - default: true
|
||||
- `showFitView`: boolean - default: true
|
||||
- `showInteractive`: boolean - default: true
|
||||
- `onZoomIn`: function that gets triggered when the zoom in button is pressed
|
||||
- `onZoomOut`: function that gets triggered when the zoom out button is pressed
|
||||
- `onFitView`: function that gets triggered when the fit-to-view button is pressed
|
||||
- `onInteractiveChange`: function that gets triggered when the lock button is pressed - passes the new value
|
||||
- `style`: css properties
|
||||
- `className`: additional class name
|
||||
|
||||
**Typescript:** The interface of the Controls Prop Types are exported as `ControlProps`.
|
||||
|
||||
## ReactFlowProvider
|
||||
|
||||
If you need access to the internal state and action of React Flow outside of the `ReactFlow` component you can wrap it with the `ReactFlowProvider` component:
|
||||
|
||||
```javascript
|
||||
import ReactFlow, { ReactFlowProvider } from 'react-flow-renderer';
|
||||
|
||||
const FlowWithOwnProvider = () => (
|
||||
<ReactFlowProvider>
|
||||
<ReactFlow
|
||||
elements={elements}
|
||||
onElementClick={onElementClick}
|
||||
onConnect={onConnect}
|
||||
/>
|
||||
</ReactFlowProvider>
|
||||
);
|
||||
```
|
||||
|
||||
It is used in the [provider example](example/src/Provider/index.js).
|
||||
|
||||
# Styling
|
||||
|
||||
There are two ways how you can style the graph pane and the elements.
|
||||
You can create your own CSS rules or pass style properties to the components.
|
||||
|
||||
## Using Class Names
|
||||
|
||||
Since we are rendering DOM nodes you can simply overwrite the styles with your own CSS rules.
|
||||
The React Flow wrapper has the className `react-flow`. If you want to change the graph background for example you can do:
|
||||
|
||||
```css
|
||||
.react-flow {
|
||||
background: red;
|
||||
}
|
||||
```
|
||||
|
||||
### Used Class Names
|
||||
|
||||
* `.react-flow` - Outer container
|
||||
* `.react-flow__renderer` - Flow container, used for zooming and panning
|
||||
* `.react-flow__pane` - Background Pane for click and wheel events, previously `.react-flow__zoompane`
|
||||
* `.react-flow__selectionpane` - Selection pane
|
||||
* `.react-flow__selection` - User selection
|
||||
* `.react-flow__edges` - Edges wrapper
|
||||
* `.react-flow__edge` - Edge element
|
||||
* `.selected` is added when edge is selected
|
||||
* `.animated` is added when edge is animated
|
||||
* `.react-flow__edge-path` - Edge element path
|
||||
* `.react-flow__edge-text` - Edge text
|
||||
* `.react-flow__edge-textbg` - Edge text background
|
||||
* `.react-flow__connection` - Connection line
|
||||
* `.react-flow__connection-path` - Connection line path
|
||||
* `.react-flow__nodes` - Nodes wrapper
|
||||
* `.react-flow__node` - Node element
|
||||
* `.selected` is added when edge is selected
|
||||
* `-${type}` is added (`.react-flow__node-default`, `.react-flow__node-input`, `.react-flow__node-output`)
|
||||
* `.react-flow__nodesselection` - Nodes selection
|
||||
* `.react-flow__nodesselection-rect ` - Nodes selection rect
|
||||
* `.react-flow__handle` - Handle component
|
||||
* `.react-flow__handle-bottom` is added when position = 'bottom'
|
||||
* `.react-flow__handle-top` is added when position = 'top'
|
||||
* `.react-flow__handle-left` is added when position = 'left'
|
||||
* `.react-flow__handle-right` is added when position = 'right'
|
||||
* `.react-flow__handle-connecting` is added when connection line is above a handle
|
||||
* `.react-flow__handle-valid` is added when connection line is above a handle and the connection is valid
|
||||
* `.react-flow__background` - Background component
|
||||
* `.react-flow__minimap` - Mini map component
|
||||
* `.react-flow__controls` - Controls component
|
||||
|
||||
## Using Properties
|
||||
|
||||
You could achieve the same effect by passing a style prop to the `ReactFlow` component:
|
||||
|
||||
```javascript
|
||||
const style = { background: 'red', height: '300px' };
|
||||
|
||||
const FlowWithRedBg = (
|
||||
<ReactFlow
|
||||
elements={elements}
|
||||
style={style}
|
||||
/>
|
||||
);
|
||||
```
|
||||
|
||||
# Helper Functions
|
||||
|
||||
If you want to remove a node or connect two nodes with each other you need to pass a function to `onElementsRemove` and `onConnect`. In order to simplify this process there are some helper functions you can use:
|
||||
|
||||
```javascript
|
||||
import ReactFlow, { isNode, isEdge, removeElements, addEdge } from 'react-flow-renderer';
|
||||
```
|
||||
|
||||
### isEdge
|
||||
|
||||
Returns `true` if the passed element is an edge.
|
||||
|
||||
`isEdge = (element: Node | Edge): element is Edge`
|
||||
|
||||
### isNode
|
||||
|
||||
Returns `true` if the passed element is a node.
|
||||
|
||||
`isNode = (element: Node | Edge): element is Node`
|
||||
|
||||
### removeElements
|
||||
|
||||
Returns an array of elements without the ones from `elementsToRemove`. It also removes all incoming/outgoing edges if you just pass one or multiple nodes.
|
||||
|
||||
`removeElements = (elementsToRemove: Elements, elements: Elements): Elements`
|
||||
|
||||
### addEdge
|
||||
|
||||
Returns an array with elements with the added edge.
|
||||
|
||||
`addEdge = (edgeParams: Edge, elements: Elements): Elements`
|
||||
|
||||
### getOutgoers
|
||||
|
||||
Returns all direct child nodes of the passed node.
|
||||
|
||||
`getOutgoers = (node: Node, elements: Elements): Node[]`
|
||||
|
||||
### getIncomers
|
||||
|
||||
Returns all direct incoming nodes of the passed node.
|
||||
|
||||
`getIncomers = (node: Node, elements: Elements): Node[]`
|
||||
|
||||
### getConnectedEdges
|
||||
|
||||
Returns all edges that are connected to the passed nodes.
|
||||
|
||||
`getConnectedEdges = (nodes: Node[], edges: Edge[]): Edge[]`
|
||||
|
||||
You can use these function as seen in [this example](/example/src/Overview/index.js#L40-L41) or use your own ones.
|
||||
|
||||
# Access Internal State and Actions
|
||||
|
||||
Under the hood React Flow uses [Easy Peasy](https://easy-peasy.now.sh/) for state handling.
|
||||
If you need to access the internal state you can use the `useStoreState` hook inside a child component of the `ReactFlow` component:
|
||||
|
||||
```javascript
|
||||
import ReactFlow, { useStoreState } from 'react-flow-renderer';
|
||||
|
||||
const NodesDebugger = () => {
|
||||
const nodes = useStoreState(state => state.nodes);
|
||||
|
||||
console.log(nodes);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
const Flow = () => (
|
||||
<ReactFlow elements={elements}>
|
||||
<NodesDebugger />
|
||||
</ReactFlow>
|
||||
);
|
||||
```
|
||||
|
||||
You will not need this in most cases but you can also use the internal actions that are defined in the [store](/src/store/index.ts):
|
||||
|
||||
```javascript
|
||||
import React, { useEffect } from 'react';
|
||||
import { useStoreActions } from 'react-flow-renderer'
|
||||
|
||||
const TransformUpdater = ({ x, y, zoom }) => {
|
||||
const setTransform = useStoreActions(actions => actions.setInitTransform);
|
||||
|
||||
useEffect(() => {
|
||||
setTransform({ x, y, k: zoom })
|
||||
}, [x, y, zoom])
|
||||
});
|
||||
```
|
||||
|
||||
If you need more control you can wrap the `ReactFlow` component with the `ReactFlowProvider` component in order to be able to call `useStoreState` and `useStoreActions` outside of the `ReactFlow` component.
|
||||
|
||||
# Examples
|
||||
|
||||
You can find all examples in the [example](example) folder or check out the live versions:
|
||||
|
||||
- [overview](https://reactflow.dev/examples)
|
||||
- [custom node](https://reactflow.dev/examples/custom-node)
|
||||
- [horizontal](https://reactflow.dev/examples/horizontal)
|
||||
- [stress](https://reactflow.dev/examples/stress)
|
||||
- [validation](https://reactflow.dev/examples/validation)
|
||||
- [provider](https://reactflow.dev/examples/provider)
|
||||
- [edges](https://reactflow.dev/examples/edges)
|
||||
- [interaction](https://reactflow.dev/examples/interaction)
|
||||
- [provider](https://reactflow.dev/examples/provider)
|
||||
- [hidden](https://reactflow.dev/examples/hidden)
|
||||
- [edge types](https://reactflow.dev/examples/edge-types)
|
||||
- [custom connectionline](https://reactflow.dev/examples/custom-connectionline)
|
||||
|
||||
# Development
|
||||
## Development
|
||||
|
||||
You need to install the React Flow dependencies via `npm install` and the ones of the examples `cd example && npm install`.
|
||||
|
||||
If you want to contribute or develop some custom features the easiest way is to start the dev server:
|
||||
If you want to contribute or develop custom features the easiest way is to start the dev server:
|
||||
|
||||
```
|
||||
npm run dev
|
||||
@@ -691,7 +42,7 @@ npm run dev
|
||||
|
||||
This serves the content of the `example` folder and watches changes inside the `src` folder. The examples are using the source of the `src` folder.
|
||||
|
||||
# Testing
|
||||
## Testing
|
||||
|
||||
Testing is done with cypress. You can find the tests in the [`integration/flow`](/cypress/integration/flow) folder. In order to run the tests do:
|
||||
|
||||
@@ -699,10 +50,14 @@ Testing is done with cypress. You can find the tests in the [`integration/flow`]
|
||||
npm run test
|
||||
```
|
||||
|
||||
## Thanks!
|
||||
## Support
|
||||
|
||||
If you need custom support or features for your application we are [happy to hear from you](https://webkid.io/contact).
|
||||
|
||||
### Thanks!
|
||||
|
||||
Special thanks to [Andy Lindemann](https://github.com/AndyLnd) for a lot of helpful contributions!
|
||||
|
||||
---
|
||||
|
||||
React Flow was initially developed by [webkid](https://webkid.io), a data visualization company from Berlin. If you need help or want to develop react-based tools or data visualizations, get in touch!
|
||||
React Flow is maintained by [webkid](https://webkid.io), a data visualization agency from Berlin. If you need help or want to develop react-based tools or data visualizations, [get in touch](https://webkid.io/contact)!
|
||||
|
||||
@@ -44,12 +44,19 @@ describe('Graph Utils Testing', () => {
|
||||
|
||||
describe('tests addEdge function', () => {
|
||||
it('adds edge', () => {
|
||||
const newEdge = { source: '2', target: '3' };
|
||||
const newEdge = { source: '1', target: '4' };
|
||||
const nextElements = addEdge(newEdge, elements);
|
||||
|
||||
expect(nextElements.length).to.be.equal(elements.length + 1);
|
||||
});
|
||||
|
||||
it('tries to add existing edge', () => {
|
||||
const newEdge = { source: '2', target: '3' };
|
||||
const nextElements = addEdge(newEdge, elements);
|
||||
|
||||
expect(nextElements.length).to.be.equal(elements.length);
|
||||
});
|
||||
|
||||
it('tries to add invalid edge', () => {
|
||||
const newEdge = { nosource: '1', notarget: '3' };
|
||||
|
||||
@@ -61,18 +68,6 @@ describe('Graph Utils Testing', () => {
|
||||
expect(e.message).to.be.equal("Can't create edge. An edge needs a source and a target.");
|
||||
}
|
||||
});
|
||||
|
||||
it('tries to add edge with id that does not exist', () => {
|
||||
const notExistingId = 'does-not-exist';
|
||||
const newEdge = { source: notExistingId, target: '3' };
|
||||
|
||||
try {
|
||||
addEdge(newEdge, elements);
|
||||
} catch (e) {
|
||||
console.log(e.message);
|
||||
expect(e.message).to.be.equal(`Can't create edge. Node with id=${notExistingId} does not exist.`);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('tests removeElements function', () => {
|
||||
|
||||
@@ -49,7 +49,7 @@ describe('Interaction Flow Rendering', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('tries to zoom by scroll', () => {
|
||||
it('tries to zoom by double click', () => {
|
||||
const styleBeforeZoom = Cypress.$('.react-flow__nodes').css('transform');
|
||||
|
||||
cy.get('.react-flow__renderer')
|
||||
@@ -60,7 +60,7 @@ describe('Interaction Flow Rendering', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('tries to zoom by double click', () => {
|
||||
it('tries to zoom by scroll', () => {
|
||||
const styleBeforeZoom = Cypress.$('.react-flow__nodes').css('transform');
|
||||
|
||||
cy.get('.react-flow__renderer')
|
||||
|
||||
@@ -56,8 +56,8 @@ const CustomNodeFlow = () => {
|
||||
{ id: '4', type: 'output', data: { label: 'Output B' }, position: { x: 550, y: 100 }, targetPosition: 'left' },
|
||||
|
||||
{ id: 'e1-2', source: '1', target: '2', animated: true, style: { stroke: '#fff' } },
|
||||
{ id: 'e2a-3', source: '2__a', target: '3', animated: true, style: { stroke: '#fff' } },
|
||||
{ id: 'e2b-4', source: '2__b', target: '4', animated: true, style: { stroke: '#fff' } },
|
||||
{ id: 'e2a-3', source: '2', sourceHandle: 'a', target: '3', animated: true, style: { stroke: '#fff' } },
|
||||
{ id: 'e2b-4', source: '2', sourceHandle: 'b', target: '4', animated: true, style: { stroke: '#fff' } },
|
||||
]);
|
||||
}, []);
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ const InteractionFlow = () => {
|
||||
const [isDraggable, setIsDraggable] = useState(false);
|
||||
const [isConnectable, setIsConnectable] = useState(false);
|
||||
const [zoomOnScroll, setZoomOnScroll] = useState(false);
|
||||
const [panOnScroll, setPanOnScroll] = useState(false);
|
||||
const [zoomOnDoubleClick, setZoomOnDoubleClick] = useState(false);
|
||||
const [paneMoveable, setPaneMoveable] = useState(true);
|
||||
const [captureZoomClick, setCaptureZoomClick] = useState(false);
|
||||
@@ -39,6 +40,7 @@ const InteractionFlow = () => {
|
||||
nodesConnectable={isConnectable}
|
||||
nodesDraggable={isDraggable}
|
||||
zoomOnScroll={zoomOnScroll}
|
||||
panOnScroll={panOnScroll}
|
||||
zoomOnDoubleClick={zoomOnDoubleClick}
|
||||
onConnect={onConnect}
|
||||
onElementClick={captureElementClick ? onElementClick : undefined}
|
||||
@@ -55,7 +57,7 @@ const InteractionFlow = () => {
|
||||
<div style={{ position: 'absolute', left: 10, top: 10, zIndex: 4 }}>
|
||||
<div>
|
||||
<label htmlFor="draggable">
|
||||
draggable
|
||||
nodesDraggable
|
||||
<input
|
||||
id="draggable"
|
||||
type="checkbox"
|
||||
@@ -67,7 +69,7 @@ const InteractionFlow = () => {
|
||||
</div>
|
||||
<div>
|
||||
<label htmlFor="connectable">
|
||||
connectable
|
||||
nodesConnectable
|
||||
<input
|
||||
id="connectable"
|
||||
type="checkbox"
|
||||
@@ -79,7 +81,7 @@ const InteractionFlow = () => {
|
||||
</div>
|
||||
<div>
|
||||
<label htmlFor="selectable">
|
||||
selectable
|
||||
elementsSelectable
|
||||
<input
|
||||
id="selectable"
|
||||
type="checkbox"
|
||||
@@ -91,7 +93,7 @@ const InteractionFlow = () => {
|
||||
</div>
|
||||
<div>
|
||||
<label htmlFor="zoomonscroll">
|
||||
zoom on scroll
|
||||
zoomOnScroll
|
||||
<input
|
||||
id="zoomonscroll"
|
||||
type="checkbox"
|
||||
@@ -101,9 +103,21 @@ const InteractionFlow = () => {
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
<div>
|
||||
<label htmlFor="panonscroll">
|
||||
panOnScroll
|
||||
<input
|
||||
id="panonscroll"
|
||||
type="checkbox"
|
||||
checked={panOnScroll}
|
||||
onChange={(event) => setPanOnScroll(event.target.checked)}
|
||||
className="react-flow__panonscroll"
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
<div>
|
||||
<label htmlFor="zoomondbl">
|
||||
zoom on double click
|
||||
zoomOnDoubleClick
|
||||
<input
|
||||
id="zoomondbl"
|
||||
type="checkbox"
|
||||
@@ -115,7 +129,7 @@ const InteractionFlow = () => {
|
||||
</div>
|
||||
<div>
|
||||
<label htmlFor="panemoveable">
|
||||
pane moveable
|
||||
paneMoveable
|
||||
<input
|
||||
id="panemoveable"
|
||||
type="checkbox"
|
||||
@@ -127,7 +141,7 @@ const InteractionFlow = () => {
|
||||
</div>
|
||||
<div>
|
||||
<label htmlFor="capturezoompaneclick">
|
||||
capture zoom pane click
|
||||
capture onPaneClick
|
||||
<input
|
||||
id="capturezoompaneclick"
|
||||
type="checkbox"
|
||||
@@ -139,7 +153,7 @@ const InteractionFlow = () => {
|
||||
</div>
|
||||
<div>
|
||||
<label htmlFor="capturezoompanescroll">
|
||||
capture zoom pane scroll
|
||||
capture onPaneScroll
|
||||
<input
|
||||
id="capturezoompanescroll"
|
||||
type="checkbox"
|
||||
@@ -151,7 +165,7 @@ const InteractionFlow = () => {
|
||||
</div>
|
||||
<div>
|
||||
<label htmlFor="captureelementclick">
|
||||
capture element click
|
||||
capture onElementClick
|
||||
<input
|
||||
id="captureelementclick"
|
||||
type="checkbox"
|
||||
|
||||
@@ -158,7 +158,7 @@ const OverviewFlow = () => {
|
||||
|
||||
return '#fff';
|
||||
}}
|
||||
borderRadius={2}
|
||||
nodeBorderRadius={2}
|
||||
/>
|
||||
<Controls />
|
||||
<Background color="#aaa" gap={16} />
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "react-flow-renderer",
|
||||
"version": "6.2.2",
|
||||
"version": "7.0.0-next.0",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
@@ -362,14 +362,14 @@
|
||||
}
|
||||
},
|
||||
"@babel/plugin-proposal-decorators": {
|
||||
"version": "7.8.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.8.3.tgz",
|
||||
"integrity": "sha512-e3RvdvS4qPJVTe288DlXjwKflpfy1hr0j5dz5WpIYYeP7vQZg2WfAEIp8k5/Lwis/m5REXEteIz6rrcDtXXG7w==",
|
||||
"version": "7.12.1",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.12.1.tgz",
|
||||
"integrity": "sha512-knNIuusychgYN8fGJHONL0RbFxLGawhXOJNLBk75TniTsZZeA+wdkDuv6wp4lGwzQEKjZi6/WYtnb3udNPmQmQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/helper-create-class-features-plugin": "^7.8.3",
|
||||
"@babel/helper-plugin-utils": "^7.8.3",
|
||||
"@babel/plugin-syntax-decorators": "^7.8.3"
|
||||
"@babel/helper-create-class-features-plugin": "^7.12.1",
|
||||
"@babel/helper-plugin-utils": "^7.10.4",
|
||||
"@babel/plugin-syntax-decorators": "^7.12.1"
|
||||
}
|
||||
},
|
||||
"@babel/plugin-proposal-dynamic-import": {
|
||||
@@ -730,13 +730,13 @@
|
||||
}
|
||||
},
|
||||
"@babel/plugin-transform-flow-strip-types": {
|
||||
"version": "7.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.9.0.tgz",
|
||||
"integrity": "sha512-7Qfg0lKQhEHs93FChxVLAvhBshOPQDtJUTVHr/ZwQNRccCm4O9D79r9tVSoV8iNwjP1YgfD+e/fgHcPkN1qEQg==",
|
||||
"version": "7.12.1",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.12.1.tgz",
|
||||
"integrity": "sha512-8hAtkmsQb36yMmEtk2JZ9JnVyDSnDOdlB+0nEGzIDLuK4yR3JcEjfuFPYkdEPSh8Id+rAMeBEn+X0iVEyho6Hg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/helper-plugin-utils": "^7.8.3",
|
||||
"@babel/plugin-syntax-flow": "^7.8.3"
|
||||
"@babel/helper-plugin-utils": "^7.10.4",
|
||||
"@babel/plugin-syntax-flow": "^7.12.1"
|
||||
}
|
||||
},
|
||||
"@babel/plugin-transform-for-of": {
|
||||
@@ -1147,13 +1147,13 @@
|
||||
}
|
||||
},
|
||||
"@babel/preset-typescript": {
|
||||
"version": "7.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.9.0.tgz",
|
||||
"integrity": "sha512-S4cueFnGrIbvYJgwsVFKdvOmpiL0XGw9MFW9D0vgRys5g36PBhZRL8NX8Gr2akz8XRtzq6HuDXPD/1nniagNUg==",
|
||||
"version": "7.12.1",
|
||||
"resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.12.1.tgz",
|
||||
"integrity": "sha512-hNK/DhmoJPsksdHuI/RVrcEws7GN5eamhi28JkO52MqIxU8Z0QpmiSOQxZHWOHV7I3P4UjHV97ay4TcamMA6Kw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/helper-plugin-utils": "^7.8.3",
|
||||
"@babel/plugin-transform-typescript": "^7.9.0"
|
||||
"@babel/helper-plugin-utils": "^7.10.4",
|
||||
"@babel/plugin-transform-typescript": "^7.12.1"
|
||||
}
|
||||
},
|
||||
"@babel/runtime": {
|
||||
@@ -1503,9 +1503,9 @@
|
||||
}
|
||||
},
|
||||
"@rollup/plugin-node-resolve": {
|
||||
"version": "9.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-9.0.0.tgz",
|
||||
"integrity": "sha512-gPz+utFHLRrd41WMP13Jq5mqqzHL3OXrfj3/MkSyB6UBIcuNt9j60GCbarzMzdf1VHFpOxfQh/ez7wyadLMqkg==",
|
||||
"version": "10.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-10.0.0.tgz",
|
||||
"integrity": "sha512-sNijGta8fqzwA1VwUEtTvWCx2E7qC70NMsDh4ZG13byAXYigBNZMxALhKUSycBks5gupJdq0lFrKumFrRZ8H3A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@rollup/pluginutils": "^3.1.0",
|
||||
@@ -2071,9 +2071,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"@welldone-software/why-did-you-render": {
|
||||
"version": "4.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@welldone-software/why-did-you-render/-/why-did-you-render-4.3.2.tgz",
|
||||
"integrity": "sha512-pzaFmUUH+FjbsDgcMB1xNPncIaxibvo0FLbGySLIh+v+7Bp5pqSJFO6zlqo4orPLm3nAdYEgL2vrlj8befoypg==",
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@welldone-software/why-did-you-render/-/why-did-you-render-5.0.0.tgz",
|
||||
"integrity": "sha512-A6xUP/55vJQwA1+L6iZbG81cQanSQQVR15yPcjLIp6lHmybXEOXsYcuXaDZHYqiNStZRzv64YPcYJC9wdphfhw==",
|
||||
"requires": {
|
||||
"lodash": "^4"
|
||||
}
|
||||
@@ -2173,9 +2173,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"arch": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/arch/-/arch-2.1.2.tgz",
|
||||
"integrity": "sha512-NTBIIbAfkJeIletyABbVtdPgeKfDafR+1mZV/AyyfC1UkVkp9iUjV+wwmqtUgphHYajbI86jejBJp5e+jkGTiQ==",
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/arch/-/arch-2.2.0.tgz",
|
||||
"integrity": "sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==",
|
||||
"dev": true
|
||||
},
|
||||
"argparse": {
|
||||
@@ -2263,9 +2263,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"aws4": {
|
||||
"version": "1.10.1",
|
||||
"resolved": "https://registry.npmjs.org/aws4/-/aws4-1.10.1.tgz",
|
||||
"integrity": "sha512-zg7Hz2k5lI8kb7U32998pRRFin7zJlkfezGJjUc2heaD4Pw2wObakCDVzkKztTm/Ln7eiVvYsjqak0Ed4LkMDA==",
|
||||
"version": "1.11.0",
|
||||
"resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz",
|
||||
"integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==",
|
||||
"dev": true
|
||||
},
|
||||
"axios": {
|
||||
@@ -2304,204 +2304,26 @@
|
||||
"dev": true
|
||||
},
|
||||
"babel-preset-react-app": {
|
||||
"version": "9.1.2",
|
||||
"resolved": "https://registry.npmjs.org/babel-preset-react-app/-/babel-preset-react-app-9.1.2.tgz",
|
||||
"integrity": "sha512-k58RtQOKH21NyKtzptoAvtAODuAJJs3ZhqBMl456/GnXEQ/0La92pNmwgWoMn5pBTrsvk3YYXdY7zpY4e3UIxA==",
|
||||
"version": "10.0.0",
|
||||
"resolved": "https://registry.npmjs.org/babel-preset-react-app/-/babel-preset-react-app-10.0.0.tgz",
|
||||
"integrity": "sha512-itL2z8v16khpuKutx5IH8UdCdSTuzrOhRFTEdIhveZ2i1iBKDrVE0ATa4sFVy+02GLucZNVBWtoarXBy0Msdpg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/core": "7.9.0",
|
||||
"@babel/plugin-proposal-class-properties": "7.8.3",
|
||||
"@babel/plugin-proposal-decorators": "7.8.3",
|
||||
"@babel/plugin-proposal-nullish-coalescing-operator": "7.8.3",
|
||||
"@babel/plugin-proposal-numeric-separator": "7.8.3",
|
||||
"@babel/plugin-proposal-optional-chaining": "7.9.0",
|
||||
"@babel/plugin-transform-flow-strip-types": "7.9.0",
|
||||
"@babel/plugin-transform-react-display-name": "7.8.3",
|
||||
"@babel/plugin-transform-runtime": "7.9.0",
|
||||
"@babel/preset-env": "7.9.0",
|
||||
"@babel/preset-react": "7.9.1",
|
||||
"@babel/preset-typescript": "7.9.0",
|
||||
"@babel/runtime": "7.9.0",
|
||||
"@babel/core": "7.12.3",
|
||||
"@babel/plugin-proposal-class-properties": "7.12.1",
|
||||
"@babel/plugin-proposal-decorators": "7.12.1",
|
||||
"@babel/plugin-proposal-nullish-coalescing-operator": "7.12.1",
|
||||
"@babel/plugin-proposal-numeric-separator": "7.12.1",
|
||||
"@babel/plugin-proposal-optional-chaining": "7.12.1",
|
||||
"@babel/plugin-transform-flow-strip-types": "7.12.1",
|
||||
"@babel/plugin-transform-react-display-name": "7.12.1",
|
||||
"@babel/plugin-transform-runtime": "7.12.1",
|
||||
"@babel/preset-env": "7.12.1",
|
||||
"@babel/preset-react": "7.12.1",
|
||||
"@babel/preset-typescript": "7.12.1",
|
||||
"@babel/runtime": "7.12.1",
|
||||
"babel-plugin-macros": "2.8.0",
|
||||
"babel-plugin-transform-react-remove-prop-types": "0.4.24"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/core": {
|
||||
"version": "7.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.9.0.tgz",
|
||||
"integrity": "sha512-kWc7L0fw1xwvI0zi8OKVBuxRVefwGOrKSQMvrQ3dW+bIIavBY3/NpXmpjMy7bQnLgwgzWQZ8TlM57YHpHNHz4w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/code-frame": "^7.8.3",
|
||||
"@babel/generator": "^7.9.0",
|
||||
"@babel/helper-module-transforms": "^7.9.0",
|
||||
"@babel/helpers": "^7.9.0",
|
||||
"@babel/parser": "^7.9.0",
|
||||
"@babel/template": "^7.8.6",
|
||||
"@babel/traverse": "^7.9.0",
|
||||
"@babel/types": "^7.9.0",
|
||||
"convert-source-map": "^1.7.0",
|
||||
"debug": "^4.1.0",
|
||||
"gensync": "^1.0.0-beta.1",
|
||||
"json5": "^2.1.2",
|
||||
"lodash": "^4.17.13",
|
||||
"resolve": "^1.3.2",
|
||||
"semver": "^5.4.1",
|
||||
"source-map": "^0.5.0"
|
||||
}
|
||||
},
|
||||
"@babel/plugin-proposal-class-properties": {
|
||||
"version": "7.8.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.8.3.tgz",
|
||||
"integrity": "sha512-EqFhbo7IosdgPgZggHaNObkmO1kNUe3slaKu54d5OWvy+p9QIKOzK1GAEpAIsZtWVtPXUHSMcT4smvDrCfY4AA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/helper-create-class-features-plugin": "^7.8.3",
|
||||
"@babel/helper-plugin-utils": "^7.8.3"
|
||||
}
|
||||
},
|
||||
"@babel/plugin-proposal-nullish-coalescing-operator": {
|
||||
"version": "7.8.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.8.3.tgz",
|
||||
"integrity": "sha512-TS9MlfzXpXKt6YYomudb/KU7nQI6/xnapG6in1uZxoxDghuSMZsPb6D2fyUwNYSAp4l1iR7QtFOjkqcRYcUsfw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/helper-plugin-utils": "^7.8.3",
|
||||
"@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.0"
|
||||
}
|
||||
},
|
||||
"@babel/plugin-proposal-numeric-separator": {
|
||||
"version": "7.8.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.8.3.tgz",
|
||||
"integrity": "sha512-jWioO1s6R/R+wEHizfaScNsAx+xKgwTLNXSh7tTC4Usj3ItsPEhYkEpU4h+lpnBwq7NBVOJXfO6cRFYcX69JUQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/helper-plugin-utils": "^7.8.3",
|
||||
"@babel/plugin-syntax-numeric-separator": "^7.8.3"
|
||||
}
|
||||
},
|
||||
"@babel/plugin-proposal-optional-chaining": {
|
||||
"version": "7.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.9.0.tgz",
|
||||
"integrity": "sha512-NDn5tu3tcv4W30jNhmc2hyD5c56G6cXx4TesJubhxrJeCvuuMpttxr0OnNCqbZGhFjLrg+NIhxxC+BK5F6yS3w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/helper-plugin-utils": "^7.8.3",
|
||||
"@babel/plugin-syntax-optional-chaining": "^7.8.0"
|
||||
}
|
||||
},
|
||||
"@babel/plugin-transform-react-display-name": {
|
||||
"version": "7.8.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.8.3.tgz",
|
||||
"integrity": "sha512-3Jy/PCw8Fe6uBKtEgz3M82ljt+lTg+xJaM4og+eyu83qLT87ZUSckn0wy7r31jflURWLO83TW6Ylf7lyXj3m5A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/helper-plugin-utils": "^7.8.3"
|
||||
}
|
||||
},
|
||||
"@babel/plugin-transform-runtime": {
|
||||
"version": "7.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.9.0.tgz",
|
||||
"integrity": "sha512-pUu9VSf3kI1OqbWINQ7MaugnitRss1z533436waNXp+0N3ur3zfut37sXiQMxkuCF4VUjwZucen/quskCh7NHw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/helper-module-imports": "^7.8.3",
|
||||
"@babel/helper-plugin-utils": "^7.8.3",
|
||||
"resolve": "^1.8.1",
|
||||
"semver": "^5.5.1"
|
||||
}
|
||||
},
|
||||
"@babel/preset-env": {
|
||||
"version": "7.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.9.0.tgz",
|
||||
"integrity": "sha512-712DeRXT6dyKAM/FMbQTV/FvRCms2hPCx+3weRjZ8iQVQWZejWWk1wwG6ViWMyqb/ouBbGOl5b6aCk0+j1NmsQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/compat-data": "^7.9.0",
|
||||
"@babel/helper-compilation-targets": "^7.8.7",
|
||||
"@babel/helper-module-imports": "^7.8.3",
|
||||
"@babel/helper-plugin-utils": "^7.8.3",
|
||||
"@babel/plugin-proposal-async-generator-functions": "^7.8.3",
|
||||
"@babel/plugin-proposal-dynamic-import": "^7.8.3",
|
||||
"@babel/plugin-proposal-json-strings": "^7.8.3",
|
||||
"@babel/plugin-proposal-nullish-coalescing-operator": "^7.8.3",
|
||||
"@babel/plugin-proposal-numeric-separator": "^7.8.3",
|
||||
"@babel/plugin-proposal-object-rest-spread": "^7.9.0",
|
||||
"@babel/plugin-proposal-optional-catch-binding": "^7.8.3",
|
||||
"@babel/plugin-proposal-optional-chaining": "^7.9.0",
|
||||
"@babel/plugin-proposal-unicode-property-regex": "^7.8.3",
|
||||
"@babel/plugin-syntax-async-generators": "^7.8.0",
|
||||
"@babel/plugin-syntax-dynamic-import": "^7.8.0",
|
||||
"@babel/plugin-syntax-json-strings": "^7.8.0",
|
||||
"@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.0",
|
||||
"@babel/plugin-syntax-numeric-separator": "^7.8.0",
|
||||
"@babel/plugin-syntax-object-rest-spread": "^7.8.0",
|
||||
"@babel/plugin-syntax-optional-catch-binding": "^7.8.0",
|
||||
"@babel/plugin-syntax-optional-chaining": "^7.8.0",
|
||||
"@babel/plugin-syntax-top-level-await": "^7.8.3",
|
||||
"@babel/plugin-transform-arrow-functions": "^7.8.3",
|
||||
"@babel/plugin-transform-async-to-generator": "^7.8.3",
|
||||
"@babel/plugin-transform-block-scoped-functions": "^7.8.3",
|
||||
"@babel/plugin-transform-block-scoping": "^7.8.3",
|
||||
"@babel/plugin-transform-classes": "^7.9.0",
|
||||
"@babel/plugin-transform-computed-properties": "^7.8.3",
|
||||
"@babel/plugin-transform-destructuring": "^7.8.3",
|
||||
"@babel/plugin-transform-dotall-regex": "^7.8.3",
|
||||
"@babel/plugin-transform-duplicate-keys": "^7.8.3",
|
||||
"@babel/plugin-transform-exponentiation-operator": "^7.8.3",
|
||||
"@babel/plugin-transform-for-of": "^7.9.0",
|
||||
"@babel/plugin-transform-function-name": "^7.8.3",
|
||||
"@babel/plugin-transform-literals": "^7.8.3",
|
||||
"@babel/plugin-transform-member-expression-literals": "^7.8.3",
|
||||
"@babel/plugin-transform-modules-amd": "^7.9.0",
|
||||
"@babel/plugin-transform-modules-commonjs": "^7.9.0",
|
||||
"@babel/plugin-transform-modules-systemjs": "^7.9.0",
|
||||
"@babel/plugin-transform-modules-umd": "^7.9.0",
|
||||
"@babel/plugin-transform-named-capturing-groups-regex": "^7.8.3",
|
||||
"@babel/plugin-transform-new-target": "^7.8.3",
|
||||
"@babel/plugin-transform-object-super": "^7.8.3",
|
||||
"@babel/plugin-transform-parameters": "^7.8.7",
|
||||
"@babel/plugin-transform-property-literals": "^7.8.3",
|
||||
"@babel/plugin-transform-regenerator": "^7.8.7",
|
||||
"@babel/plugin-transform-reserved-words": "^7.8.3",
|
||||
"@babel/plugin-transform-shorthand-properties": "^7.8.3",
|
||||
"@babel/plugin-transform-spread": "^7.8.3",
|
||||
"@babel/plugin-transform-sticky-regex": "^7.8.3",
|
||||
"@babel/plugin-transform-template-literals": "^7.8.3",
|
||||
"@babel/plugin-transform-typeof-symbol": "^7.8.4",
|
||||
"@babel/plugin-transform-unicode-regex": "^7.8.3",
|
||||
"@babel/preset-modules": "^0.1.3",
|
||||
"@babel/types": "^7.9.0",
|
||||
"browserslist": "^4.9.1",
|
||||
"core-js-compat": "^3.6.2",
|
||||
"invariant": "^2.2.2",
|
||||
"levenary": "^1.1.1",
|
||||
"semver": "^5.5.0"
|
||||
}
|
||||
},
|
||||
"@babel/preset-react": {
|
||||
"version": "7.9.1",
|
||||
"resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.9.1.tgz",
|
||||
"integrity": "sha512-aJBYF23MPj0RNdp/4bHnAP0NVqqZRr9kl0NAOP4nJCex6OYVio59+dnQzsAWFuogdLyeaKA1hmfUIVZkY5J+TQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/helper-plugin-utils": "^7.8.3",
|
||||
"@babel/plugin-transform-react-display-name": "^7.8.3",
|
||||
"@babel/plugin-transform-react-jsx": "^7.9.1",
|
||||
"@babel/plugin-transform-react-jsx-development": "^7.9.0",
|
||||
"@babel/plugin-transform-react-jsx-self": "^7.9.0",
|
||||
"@babel/plugin-transform-react-jsx-source": "^7.9.0"
|
||||
}
|
||||
},
|
||||
"@babel/runtime": {
|
||||
"version": "7.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.9.0.tgz",
|
||||
"integrity": "sha512-cTIudHnzuWLS56ik4DnRnqqNf8MkdUzV4iFFI1h7Jo9xvrpQROYaAnaSd2mHLQAzzZAPfATynX5ord6YlNYNMA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"regenerator-runtime": "^0.13.4"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"balanced-match": {
|
||||
@@ -3426,9 +3248,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"cypress": {
|
||||
"version": "5.4.0",
|
||||
"resolved": "https://registry.npmjs.org/cypress/-/cypress-5.4.0.tgz",
|
||||
"integrity": "sha512-BJR+u3DRSYMqaBS1a3l1rbh5AkMRHugbxcYYzkl+xYlO6dzcJVE8uAhghzVI/hxijCyBg1iuSe4TRp/g1PUg8Q==",
|
||||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/cypress/-/cypress-5.5.0.tgz",
|
||||
"integrity": "sha512-UHEiTca8AUTevbT2pWkHQlxoHtXmbq+h6Eiu/Mz8DqpNkF98zjTBLv/HFiKJUU5rQzp9EwSWtms33p5TWCJ8tQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@cypress/listr-verbose-renderer": "^0.4.1",
|
||||
@@ -3461,10 +3283,10 @@
|
||||
"minimist": "^1.2.5",
|
||||
"moment": "^2.27.0",
|
||||
"ospath": "^1.2.2",
|
||||
"pretty-bytes": "^5.3.0",
|
||||
"pretty-bytes": "^5.4.1",
|
||||
"ramda": "~0.26.1",
|
||||
"request-progress": "^3.0.0",
|
||||
"supports-color": "^7.1.0",
|
||||
"supports-color": "^7.2.0",
|
||||
"tmp": "~0.2.1",
|
||||
"untildify": "^4.0.0",
|
||||
"url": "^0.11.0",
|
||||
@@ -4697,15 +4519,6 @@
|
||||
"integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==",
|
||||
"dev": true
|
||||
},
|
||||
"invariant": {
|
||||
"version": "2.2.4",
|
||||
"resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
|
||||
"integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"loose-envify": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"is-absolute-url": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-2.1.0.tgz",
|
||||
@@ -5060,13 +4873,21 @@
|
||||
}
|
||||
},
|
||||
"jsonfile": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.0.1.tgz",
|
||||
"integrity": "sha512-jR2b5v7d2vIOust+w3wtFKZIfpC2pnRmFAhAC/BuweZFQR8qZzxH1OyrQ10HmdVYiXWkYUqPVsz91cG7EL2FBg==",
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
|
||||
"integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"graceful-fs": "^4.1.6",
|
||||
"universalify": "^1.0.0"
|
||||
"universalify": "^2.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"universalify": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz",
|
||||
"integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"jsprim": {
|
||||
@@ -5120,21 +4941,6 @@
|
||||
"integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=",
|
||||
"dev": true
|
||||
},
|
||||
"leven": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz",
|
||||
"integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==",
|
||||
"dev": true
|
||||
},
|
||||
"levenary": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/levenary/-/levenary-1.1.1.tgz",
|
||||
"integrity": "sha512-mkAdOIt79FD6irqjYSs4rdbnlT5vRonMEvBVPVb3XmevfS8kgRXwfes0dhPdEtzTWD/1eNE/Bm/G1iRt6DcnQQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"leven": "^3.1.0"
|
||||
}
|
||||
},
|
||||
"lines-and-columns": {
|
||||
"version": "1.1.6",
|
||||
"resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "react-flow-renderer",
|
||||
"version": "6.2.2",
|
||||
"version": "7.0.0-next.0",
|
||||
"main": "dist/ReactFlow.js",
|
||||
"module": "dist/ReactFlow.esm.js",
|
||||
"types": "dist/index.d.ts",
|
||||
@@ -16,18 +16,18 @@
|
||||
"start:dev": "npm run build && cd example && npm start",
|
||||
"dev:wait": "start-server-and-test start:dev http-get://localhost:3000",
|
||||
"build:dev": "npm install && npm run build && cd example && npm install && npm run build",
|
||||
"build:website": "cd website && npm install && GATSBY_EXPERIMENTAL_PAGE_BUILD_ON_DATA_CHANGES=true npm run build",
|
||||
"cy:open": "cypress open",
|
||||
"cypress": "npm run dev:wait cy:open",
|
||||
"test:chrome": "cypress run --browser chrome --headless",
|
||||
"test:firefox": "cypress run --browser firefox",
|
||||
"test:all": "npm run test:chrome && npm run test:firefox",
|
||||
"test": "npm run dev:wait test:chrome",
|
||||
"release": "release-it"
|
||||
"release": "npm run test && release-it",
|
||||
"release:next": "release-it major --preRelease=next"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.12.1",
|
||||
"@welldone-software/why-did-you-render": "^4.3.2",
|
||||
"@welldone-software/why-did-you-render": "^5.0.0",
|
||||
"classcat": "^4.1.0",
|
||||
"d3-selection": "^2.0.0",
|
||||
"d3-zoom": "^2.0.0",
|
||||
@@ -43,7 +43,7 @@
|
||||
"@babel/preset-react": "^7.12.1",
|
||||
"@rollup/plugin-babel": "^5.2.1",
|
||||
"@rollup/plugin-commonjs": "^15.1.0",
|
||||
"@rollup/plugin-node-resolve": "^9.0.0",
|
||||
"@rollup/plugin-node-resolve": "^10.0.0",
|
||||
"@rollup/plugin-replace": "^2.3.4",
|
||||
"@svgr/rollup": "^5.4.0",
|
||||
"@types/classnames": "^2.2.11",
|
||||
@@ -51,8 +51,8 @@
|
||||
"@types/react": "^16.9.55",
|
||||
"@types/react-dom": "^16.9.9",
|
||||
"autoprefixer": "^9.8.6",
|
||||
"babel-preset-react-app": "^9.1.2",
|
||||
"cypress": "^5.4.0",
|
||||
"babel-preset-react-app": "^10.0.0",
|
||||
"cypress": "^5.5.0",
|
||||
"postcss-nested": "^4.2.3",
|
||||
"prettier": "2.1.2",
|
||||
"prop-types": "^15.7.2",
|
||||
|
||||
@@ -15,6 +15,7 @@ import {
|
||||
|
||||
interface ConnectionLineProps {
|
||||
connectionNodeId: ElementId;
|
||||
connectionHandleId: ElementId | null;
|
||||
connectionHandleType: HandleType;
|
||||
connectionPositionX: number;
|
||||
connectionPositionY: number;
|
||||
@@ -28,6 +29,7 @@ interface ConnectionLineProps {
|
||||
|
||||
export default ({
|
||||
connectionNodeId,
|
||||
connectionHandleId,
|
||||
connectionHandleType,
|
||||
connectionLineStyle,
|
||||
connectionPositionX,
|
||||
@@ -39,10 +41,8 @@ export default ({
|
||||
CustomConnectionLineComponent,
|
||||
}: ConnectionLineProps) => {
|
||||
const [sourceNode, setSourceNode] = useState<Node | null>(null);
|
||||
const hasHandleId = connectionNodeId.includes('__');
|
||||
const sourceIdSplitted = connectionNodeId.split('__');
|
||||
const nodeId = sourceIdSplitted[0];
|
||||
const handleId = hasHandleId ? sourceIdSplitted[1] : null;
|
||||
const nodeId = connectionNodeId;
|
||||
const handleId = connectionHandleId;
|
||||
|
||||
useEffect(() => {
|
||||
const nextSourceNode = nodes.find((n) => n.id === nodeId) || null;
|
||||
|
||||
@@ -28,7 +28,7 @@ interface BaseHandleProps {
|
||||
setConnectionNodeId: SetSourceIdFunc;
|
||||
setPosition: (pos: XYPosition) => void;
|
||||
isValidConnection: ValidConnectionFunc;
|
||||
id?: ElementId | boolean;
|
||||
id?: ElementId | null;
|
||||
className?: string;
|
||||
style?: CSSProperties;
|
||||
}
|
||||
@@ -42,6 +42,7 @@ type Result = {
|
||||
|
||||
function onMouseDown(
|
||||
event: ReactMouseEvent,
|
||||
handleId: ElementId | null,
|
||||
nodeId: ElementId,
|
||||
setConnectionNodeId: SetSourceIdFunc,
|
||||
setPosition: (pos: XYPosition) => void,
|
||||
@@ -66,7 +67,7 @@ function onMouseDown(
|
||||
x: event.clientX - containerBounds.left,
|
||||
y: event.clientY - containerBounds.top,
|
||||
});
|
||||
setConnectionNodeId({ connectionNodeId: nodeId, connectionHandleType: handleType });
|
||||
setConnectionNodeId({ connectionNodeId: nodeId, connectionHandleId: handleId, connectionHandleType: handleType });
|
||||
|
||||
if (onConnectStart) {
|
||||
onConnectStart(event, { nodeId, handleType });
|
||||
@@ -88,26 +89,33 @@ function onMouseDown(
|
||||
const result: Result = {
|
||||
elementBelow,
|
||||
isValid: false,
|
||||
connection: { source: null, target: null },
|
||||
connection: { source: null, target: null, sourceHandle: null, targetHandle: null },
|
||||
isHoveringHandle: false,
|
||||
};
|
||||
|
||||
if (elementBelow && (elementBelow.classList.contains('target') || elementBelow.classList.contains('source'))) {
|
||||
let connection: Connection = { source: null, target: null };
|
||||
|
||||
if (isTarget) {
|
||||
const sourceId = elementBelow.getAttribute('data-nodeid');
|
||||
connection = { source: sourceId, target: nodeId };
|
||||
} else {
|
||||
const targetId = elementBelow.getAttribute('data-nodeid');
|
||||
connection = { source: nodeId, target: targetId };
|
||||
}
|
||||
|
||||
const isValid = isValidConnection(connection);
|
||||
|
||||
result.connection = connection;
|
||||
result.isValid = isValid;
|
||||
result.isHoveringHandle = true;
|
||||
if (
|
||||
(isTarget && elementBelow.classList.contains('source')) ||
|
||||
(!isTarget && elementBelow.classList.contains('target'))
|
||||
) {
|
||||
let connection: Connection = { source: null, target: null, sourceHandle: null, targetHandle: null };
|
||||
|
||||
if (isTarget) {
|
||||
const sourceId = elementBelow.getAttribute('data-nodeid');
|
||||
const sourcehandleId = elementBelow.getAttribute('data-handleid');
|
||||
connection = { source: sourceId, sourceHandle: sourcehandleId, target: nodeId, targetHandle: handleId };
|
||||
} else {
|
||||
const targetId = elementBelow.getAttribute('data-nodeid');
|
||||
const targetHandleId = elementBelow.getAttribute('data-handleid');
|
||||
connection = { source: nodeId, sourceHandle: handleId, target: targetId, targetHandle: targetHandleId };
|
||||
}
|
||||
|
||||
const isValid = isValidConnection(connection);
|
||||
|
||||
result.connection = connection;
|
||||
result.isValid = isValid;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
@@ -150,7 +158,7 @@ function onMouseDown(
|
||||
}
|
||||
|
||||
resetRecentHandle();
|
||||
setConnectionNodeId({ connectionNodeId: null, connectionHandleType: null });
|
||||
setConnectionNodeId({ connectionNodeId: null, connectionHandleId: null, connectionHandleType: null });
|
||||
|
||||
document.removeEventListener('mousemove', onMouseMove);
|
||||
document.removeEventListener('mouseup', onMouseUp);
|
||||
@@ -171,7 +179,7 @@ const BaseHandle = ({
|
||||
setConnectionNodeId,
|
||||
setPosition,
|
||||
className,
|
||||
id = false,
|
||||
id = null,
|
||||
isValidConnection,
|
||||
...rest
|
||||
}: BaseHandleProps) => {
|
||||
@@ -187,17 +195,19 @@ const BaseHandle = ({
|
||||
},
|
||||
]);
|
||||
|
||||
const nodeIdWithHandleId = id ? `${nodeId}__${id}` : nodeId;
|
||||
const handleId = id || null;
|
||||
|
||||
return (
|
||||
<div
|
||||
data-nodeid={nodeIdWithHandleId}
|
||||
data-handleid={handleId}
|
||||
data-nodeid={nodeId}
|
||||
data-handlepos={position}
|
||||
className={handleClasses}
|
||||
onMouseDown={(event) =>
|
||||
onMouseDown(
|
||||
event,
|
||||
nodeIdWithHandleId,
|
||||
handleId,
|
||||
nodeId,
|
||||
setConnectionNodeId,
|
||||
setPosition,
|
||||
onConnect,
|
||||
|
||||
@@ -19,21 +19,14 @@ export const getHandleBounds = (
|
||||
(handle): HandleElement => {
|
||||
const bounds = handle.getBoundingClientRect();
|
||||
const dimensions = getDimensions(handle);
|
||||
const nodeIdAttr = handle.getAttribute('data-nodeid');
|
||||
const handleId = handle.getAttribute('data-handleid');
|
||||
const handlePosition = (handle.getAttribute('data-handlepos') as unknown) as Position;
|
||||
const nodeIdSplitted = nodeIdAttr ? nodeIdAttr.split('__') : null;
|
||||
|
||||
let handleId = null;
|
||||
|
||||
if (nodeIdSplitted) {
|
||||
handleId = (nodeIdSplitted.length ? nodeIdSplitted[1] : nodeIdSplitted) as string;
|
||||
}
|
||||
|
||||
return {
|
||||
id: handleId,
|
||||
position: handlePosition,
|
||||
x: (bounds.left - parentBounds.left) * (1 / k),
|
||||
y: (bounds.top - parentBounds.top) * (1 / k),
|
||||
x: (bounds.left - parentBounds.left) / k,
|
||||
y: (bounds.top - parentBounds.top) / k,
|
||||
...dimensions,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -98,6 +98,10 @@ function getHandle(bounds: HandleElement[], handleId: ElementId | null): HandleE
|
||||
handle = bounds.find((d) => d.id === handleId);
|
||||
}
|
||||
|
||||
if (typeof handle === 'undefined') {
|
||||
return null;
|
||||
}
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
@@ -132,18 +136,22 @@ function renderEdge(
|
||||
selectedElements: Elements | null,
|
||||
elementsSelectable: boolean
|
||||
) {
|
||||
const [sourceId, sourceHandleId] = edge.source.split('__');
|
||||
const [targetId, targetHandleId] = edge.target.split('__');
|
||||
const sourceId = edge.source;
|
||||
const sourceHandleId = edge.sourceHandle || null;
|
||||
const targetId = edge.target;
|
||||
const targetHandleId = edge.targetHandle || null;
|
||||
|
||||
const sourceNode = nodes.find((n) => n.id === sourceId);
|
||||
const targetNode = nodes.find((n) => n.id === targetId);
|
||||
|
||||
if (!sourceNode) {
|
||||
throw new Error(`couldn't create edge for source id: ${sourceId}`);
|
||||
console.warn(`couldn't create edge for source id: ${sourceId}`);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!targetNode) {
|
||||
throw new Error(`couldn't create edge for target id: ${targetId}`);
|
||||
console.warn(`couldn't create edge for target id: ${targetId}`);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!sourceNode.__rf.width || !sourceNode.__rf.height) {
|
||||
@@ -157,6 +165,16 @@ function renderEdge(
|
||||
const sourcePosition = sourceHandle ? sourceHandle.position : Position.Bottom;
|
||||
const targetPosition = targetHandle ? targetHandle.position : Position.Top;
|
||||
|
||||
if (!sourceHandle) {
|
||||
console.warn(`couldn't create edge for source handle id: ${sourceHandleId}`);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!targetHandle) {
|
||||
console.warn(`couldn't create edge for source handle id: ${targetHandleId}`);
|
||||
return null;
|
||||
}
|
||||
|
||||
const { sourceX, sourceY, targetX, targetY } = getEdgePositions(
|
||||
sourceNode,
|
||||
sourceHandle,
|
||||
@@ -208,6 +226,7 @@ const EdgeRenderer = (props: EdgeRendererProps) => {
|
||||
const edges = useStoreState((state) => state.edges);
|
||||
const nodes = useStoreState((state) => state.nodes);
|
||||
const connectionNodeId = useStoreState((state) => state.connectionNodeId);
|
||||
const connectionHandleId = useStoreState((state) => state.connectionHandleId);
|
||||
const connectionHandleType = useStoreState((state) => state.connectionHandleType);
|
||||
const connectionPosition = useStoreState((state) => state.connectionPosition);
|
||||
const selectedElements = useStoreState((state) => state.selectedElements);
|
||||
@@ -234,6 +253,7 @@ const EdgeRenderer = (props: EdgeRendererProps) => {
|
||||
<ConnectionLine
|
||||
nodes={nodes}
|
||||
connectionNodeId={connectionNodeId!}
|
||||
connectionHandleId={connectionHandleId}
|
||||
connectionHandleType={connectionHandleType!}
|
||||
connectionPositionX={connectionPosition.x}
|
||||
connectionPositionY={connectionPosition.y}
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
import React, { useCallback, useRef, memo, ReactNode, WheelEvent, MouseEvent } from 'react';
|
||||
import React, { useCallback, memo, ReactNode, WheelEvent, MouseEvent } from 'react';
|
||||
import { useStoreActions, useStoreState } from '../../store/hooks';
|
||||
|
||||
import useResizeHandler from '../../hooks/useResizeHandler';
|
||||
import useGlobalKeyHandler from '../../hooks/useGlobalKeyHandler';
|
||||
import useD3Zoom from '../../hooks/useD3Zoom';
|
||||
import useKeyPress from '../../hooks/useKeyPress';
|
||||
|
||||
import { GraphViewProps } from '../GraphView';
|
||||
import ZoomPane from '../ZoomPane';
|
||||
import UserSelection from '../../components/UserSelection';
|
||||
import NodesSelection from '../../components/NodesSelection';
|
||||
|
||||
@@ -36,7 +35,10 @@ const FlowRenderer = ({
|
||||
onMoveStart,
|
||||
onMoveEnd,
|
||||
selectionKeyCode,
|
||||
elementsSelectable,
|
||||
zoomOnScroll,
|
||||
panOnScroll,
|
||||
panOnScrollSpeed,
|
||||
zoomOnDoubleClick,
|
||||
paneMoveable,
|
||||
defaultPosition,
|
||||
@@ -47,28 +49,12 @@ const FlowRenderer = ({
|
||||
onSelectionDragStop,
|
||||
onSelectionContextMenu,
|
||||
}: FlowRendererProps) => {
|
||||
const zoomPane = useRef<HTMLDivElement>(null);
|
||||
const unsetNodesSelection = useStoreActions((actions) => actions.unsetNodesSelection);
|
||||
const nodesSelectionActive = useStoreState((state) => state.nodesSelectionActive);
|
||||
const selectionKeyPressed = useKeyPress(selectionKeyCode);
|
||||
|
||||
useResizeHandler(zoomPane);
|
||||
useGlobalKeyHandler({ onElementsRemove, deleteKeyCode });
|
||||
|
||||
useD3Zoom({
|
||||
zoomPane,
|
||||
onMove,
|
||||
onMoveStart,
|
||||
onMoveEnd,
|
||||
selectionKeyPressed,
|
||||
zoomOnScroll,
|
||||
zoomOnDoubleClick,
|
||||
paneMoveable,
|
||||
defaultPosition,
|
||||
defaultZoom,
|
||||
translateExtent,
|
||||
});
|
||||
|
||||
const onClick = useCallback(
|
||||
(event: MouseEvent) => {
|
||||
onPaneClick?.(event);
|
||||
@@ -92,7 +78,21 @@ const FlowRenderer = ({
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="react-flow__renderer" ref={zoomPane}>
|
||||
<ZoomPane
|
||||
onMove={onMove}
|
||||
onMoveStart={onMoveStart}
|
||||
onMoveEnd={onMoveEnd}
|
||||
selectionKeyPressed={selectionKeyPressed}
|
||||
elementsSelectable={elementsSelectable}
|
||||
zoomOnScroll={zoomOnScroll}
|
||||
panOnScroll={panOnScroll}
|
||||
panOnScrollSpeed={panOnScrollSpeed}
|
||||
zoomOnDoubleClick={zoomOnDoubleClick}
|
||||
paneMoveable={paneMoveable}
|
||||
defaultPosition={defaultPosition}
|
||||
defaultZoom={defaultZoom}
|
||||
translateExtent={translateExtent}
|
||||
>
|
||||
{children}
|
||||
<UserSelection selectionKeyPressed={selectionKeyPressed} />
|
||||
{nodesSelectionActive && (
|
||||
@@ -104,7 +104,7 @@ const FlowRenderer = ({
|
||||
/>
|
||||
)}
|
||||
<div className="react-flow__pane" onClick={onClick} onContextMenu={onContextMenu} onWheel={onWheel} />
|
||||
</div>
|
||||
</ZoomPane>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -70,6 +70,8 @@ export interface GraphViewProps {
|
||||
arrowHeadColor: string;
|
||||
markerEndId?: string;
|
||||
zoomOnScroll?: boolean;
|
||||
panOnScroll?: boolean;
|
||||
panOnScrollSpeed?: number;
|
||||
zoomOnDoubleClick?: boolean;
|
||||
paneMoveable?: boolean;
|
||||
}
|
||||
@@ -118,6 +120,8 @@ const GraphView = ({
|
||||
arrowHeadColor,
|
||||
markerEndId,
|
||||
zoomOnScroll,
|
||||
panOnScroll,
|
||||
panOnScrollSpeed,
|
||||
zoomOnDoubleClick,
|
||||
paneMoveable,
|
||||
onPaneClick,
|
||||
@@ -245,11 +249,14 @@ const GraphView = ({
|
||||
onElementsRemove={onElementsRemove}
|
||||
deleteKeyCode={deleteKeyCode}
|
||||
selectionKeyCode={selectionKeyCode}
|
||||
elementsSelectable={elementsSelectable}
|
||||
onMove={onMove}
|
||||
onMoveStart={onMoveStart}
|
||||
onMoveEnd={onMoveEnd}
|
||||
zoomOnScroll={zoomOnScroll}
|
||||
zoomOnDoubleClick={zoomOnDoubleClick}
|
||||
panOnScroll={panOnScroll}
|
||||
panOnScrollSpeed={panOnScrollSpeed}
|
||||
paneMoveable={paneMoveable}
|
||||
defaultPosition={defaultPosition}
|
||||
defaultZoom={defaultZoom}
|
||||
|
||||
@@ -98,6 +98,8 @@ export interface ReactFlowProps extends Omit<HTMLAttributes<HTMLDivElement>, 'on
|
||||
arrowHeadColor?: string;
|
||||
markerEndId?: string;
|
||||
zoomOnScroll?: boolean;
|
||||
panOnScroll?: boolean;
|
||||
panOnScrollSpeed?: number;
|
||||
zoomOnDoubleClick?: boolean;
|
||||
}
|
||||
|
||||
@@ -147,6 +149,8 @@ const ReactFlow = ({
|
||||
arrowHeadColor = '#b1b1b7',
|
||||
markerEndId,
|
||||
zoomOnScroll = true,
|
||||
panOnScroll = false,
|
||||
panOnScrollSpeed = 0.5,
|
||||
zoomOnDoubleClick = true,
|
||||
paneMoveable = true,
|
||||
onPaneClick,
|
||||
@@ -203,6 +207,8 @@ const ReactFlow = ({
|
||||
markerEndId={markerEndId}
|
||||
zoomOnScroll={zoomOnScroll}
|
||||
zoomOnDoubleClick={zoomOnDoubleClick}
|
||||
panOnScroll={panOnScroll}
|
||||
panOnScrollSpeed={panOnScrollSpeed}
|
||||
paneMoveable={paneMoveable}
|
||||
onPaneClick={onPaneClick}
|
||||
onPaneScroll={onPaneScroll}
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
import { useEffect, useRef, MutableRefObject } from 'react';
|
||||
import React, { useEffect, useRef, ReactNode } from 'react';
|
||||
|
||||
import { useStoreState, useStoreActions } from '../store/hooks';
|
||||
import { FlowTransform, TranslateExtent } from '../types';
|
||||
import useResizeHandler from '../../hooks/useResizeHandler';
|
||||
import { useStoreState, useStoreActions } from '../../store/hooks';
|
||||
import { FlowTransform, TranslateExtent } from '../../types';
|
||||
|
||||
interface UseD3ZoomParams {
|
||||
zoomPane: MutableRefObject<Element | null>;
|
||||
interface ZoomPaneProps {
|
||||
selectionKeyPressed: boolean;
|
||||
elementsSelectable?: boolean;
|
||||
zoomOnScroll?: boolean;
|
||||
panOnScroll?: boolean;
|
||||
panOnScrollSpeed?: number;
|
||||
zoomOnDoubleClick?: boolean;
|
||||
paneMoveable?: boolean;
|
||||
defaultPosition?: [number, number];
|
||||
@@ -15,6 +18,7 @@ interface UseD3ZoomParams {
|
||||
onMove?: (flowTransform?: FlowTransform) => void;
|
||||
onMoveStart?: (flowTransform?: FlowTransform) => void;
|
||||
onMoveEnd?: (flowTransform?: FlowTransform) => void;
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
const viewChanged = (prevTransform: FlowTransform, eventTransform: any): boolean =>
|
||||
@@ -28,31 +32,63 @@ const eventToFlowTransform = (eventTransform: any): FlowTransform => ({
|
||||
zoom: eventTransform.k,
|
||||
});
|
||||
|
||||
export default ({
|
||||
zoomPane,
|
||||
const ZoomPane = ({
|
||||
onMove,
|
||||
onMoveStart,
|
||||
onMoveEnd,
|
||||
zoomOnScroll = true,
|
||||
panOnScroll = false,
|
||||
panOnScrollSpeed = 0.5,
|
||||
zoomOnDoubleClick = true,
|
||||
selectionKeyPressed,
|
||||
elementsSelectable,
|
||||
paneMoveable = true,
|
||||
defaultPosition = [0, 0],
|
||||
defaultZoom = 1,
|
||||
translateExtent,
|
||||
}: UseD3ZoomParams): void => {
|
||||
children,
|
||||
}: ZoomPaneProps) => {
|
||||
const zoomPane = useRef<HTMLDivElement>(null);
|
||||
const prevTransform = useRef<FlowTransform>({ x: 0, y: 0, zoom: 0 });
|
||||
|
||||
const d3Zoom = useStoreState((s) => s.d3Zoom);
|
||||
const d3Selection = useStoreState((s) => s.d3Selection);
|
||||
const d3ZoomHandler = useStoreState((s) => s.d3ZoomHandler);
|
||||
|
||||
const initD3 = useStoreActions((actions) => actions.initD3);
|
||||
const updateTransform = useStoreActions((actions) => actions.updateTransform);
|
||||
|
||||
useResizeHandler(zoomPane);
|
||||
|
||||
useEffect(() => {
|
||||
if (zoomPane.current) {
|
||||
initD3({ zoomPane: zoomPane.current, defaultPosition, defaultZoom, translateExtent });
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (d3Selection && d3Zoom) {
|
||||
if (panOnScroll) {
|
||||
d3Selection
|
||||
.on('wheel', (event: any) => {
|
||||
event.preventDefault();
|
||||
event.stopImmediatePropagation();
|
||||
|
||||
const currentZoom = d3Selection.property('__zoom').k || 1;
|
||||
|
||||
d3Zoom.translateBy(
|
||||
d3Selection,
|
||||
(event.wheelDeltaX / currentZoom) * panOnScrollSpeed,
|
||||
(event.wheelDeltaY / currentZoom) * panOnScrollSpeed
|
||||
);
|
||||
})
|
||||
.on('wheel.zoom', null);
|
||||
} else if (typeof d3ZoomHandler !== 'undefined') {
|
||||
d3Selection.on('wheel', null).on('wheel.zoom', d3ZoomHandler);
|
||||
}
|
||||
}
|
||||
}, [panOnScroll, d3Selection, d3Zoom, d3ZoomHandler]);
|
||||
|
||||
useEffect(() => {
|
||||
if (d3Zoom) {
|
||||
if (selectionKeyPressed) {
|
||||
@@ -107,34 +143,52 @@ export default ({
|
||||
useEffect(() => {
|
||||
if (d3Zoom) {
|
||||
d3Zoom.filter((event: any) => {
|
||||
// if all interactions are disabled, we prevent all zoom events
|
||||
if (!paneMoveable && !zoomOnScroll && !panOnScroll && !zoomOnDoubleClick) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// during a selection we prevent all other interactions
|
||||
if (selectionKeyPressed) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// only allow zoom on nodes
|
||||
if (event.target.closest('.react-flow__node') && event.type !== 'wheel') {
|
||||
return false;
|
||||
}
|
||||
|
||||
// only allow zoom on user selection
|
||||
if (event.target.closest('.react-flow__nodesselection') && event.type !== 'wheel') {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!paneMoveable) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!zoomOnScroll && event.type === 'wheel') {
|
||||
return false;
|
||||
}
|
||||
|
||||
// if zoom on double click is disabled, we prevent the double click event
|
||||
if (!zoomOnDoubleClick && event.type === 'dblclick') {
|
||||
return false;
|
||||
}
|
||||
|
||||
// when the target element is a node, we still allow zooming
|
||||
if (event.target.closest('.react-flow__node') && event.type !== 'wheel') {
|
||||
return false;
|
||||
}
|
||||
|
||||
// when the target element is a node selection, we still allow zooming
|
||||
if (event.target.closest('.react-flow__nodesselection') && event.type !== 'wheel') {
|
||||
return false;
|
||||
}
|
||||
|
||||
// when there is no scroll handling enabled, we prevent all wheel events
|
||||
if (!zoomOnScroll && !panOnScroll && event.type === 'wheel') {
|
||||
return false;
|
||||
}
|
||||
|
||||
// if the pane is not movable, we prevent dragging it with the mouse
|
||||
if (!paneMoveable && event.type === 'mousedown') {
|
||||
return false;
|
||||
}
|
||||
|
||||
// default filter for d3-zoom, prevents zooming on buttons and when ctrl is pressed
|
||||
return !event.ctrlKey && !event.button;
|
||||
});
|
||||
}
|
||||
}, [d3Zoom, zoomOnScroll, zoomOnDoubleClick, paneMoveable, selectionKeyPressed]);
|
||||
}, [d3Zoom, zoomOnScroll, panOnScroll, zoomOnDoubleClick, paneMoveable, selectionKeyPressed, elementsSelectable]);
|
||||
|
||||
return (
|
||||
<div className="react-flow__renderer react-flow__zoompane" ref={zoomPane}>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ZoomPane;
|
||||
@@ -3,7 +3,7 @@ declare module '*.css' {
|
||||
export default content;
|
||||
}
|
||||
|
||||
interface SvgrComponent extends React.StatelessComponent<React.SVGAttributes<SVGElement>> {}
|
||||
interface SvgrComponent extends React.FunctionComponent<React.SVGAttributes<SVGElement>> {}
|
||||
|
||||
declare module '*.svg' {
|
||||
const svgUrl: string;
|
||||
@@ -13,3 +13,4 @@ declare module '*.svg' {
|
||||
}
|
||||
|
||||
declare var __REACT_FLOW_VERSION__: string;
|
||||
declare var __ENV__: string;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { createStore, Action, action, Thunk, thunk, computed, Computed } from 'easy-peasy';
|
||||
import isEqual from 'fast-deep-equal';
|
||||
import { Selection as D3Selection, ZoomBehavior } from 'd3';
|
||||
import { Selection as D3Selection, ZoomBehavior, ValueFn } from 'd3';
|
||||
import { zoom, zoomIdentity } from 'd3-zoom';
|
||||
import { select } from 'd3-selection';
|
||||
|
||||
@@ -61,6 +61,7 @@ export interface StoreModel {
|
||||
|
||||
d3Zoom: ZoomBehavior<Element, unknown> | null;
|
||||
d3Selection: D3Selection<Element, unknown, null, undefined> | null;
|
||||
d3ZoomHandler: ValueFn<Element, unknown, void> | undefined;
|
||||
d3Initialised: boolean;
|
||||
minZoom: number;
|
||||
maxZoom: number;
|
||||
@@ -72,6 +73,7 @@ export interface StoreModel {
|
||||
userSelectionRect: SelectionRect;
|
||||
|
||||
connectionNodeId: ElementId | null;
|
||||
connectionHandleId: ElementId | null;
|
||||
connectionHandleType: HandleType | null;
|
||||
connectionPosition: XYPosition;
|
||||
|
||||
@@ -157,6 +159,7 @@ export const storeModel: StoreModel = {
|
||||
d3Zoom: null,
|
||||
d3Selection: null,
|
||||
d3Initialised: false,
|
||||
d3ZoomHandler: () => {},
|
||||
minZoom: 0.5,
|
||||
maxZoom: 2,
|
||||
translateExtent: [
|
||||
@@ -177,6 +180,7 @@ export const storeModel: StoreModel = {
|
||||
draw: false,
|
||||
},
|
||||
connectionNodeId: null,
|
||||
connectionHandleId: null,
|
||||
connectionHandleType: 'source',
|
||||
connectionPosition: { x: 0, y: 0 },
|
||||
|
||||
@@ -380,11 +384,14 @@ export const storeModel: StoreModel = {
|
||||
const updatedTransform = zoomIdentity.translate(clampedX, clampedY).scale(clampedZoom);
|
||||
selection.property('__zoom', updatedTransform);
|
||||
|
||||
const defaultHandler = selection.on('wheel.zoom');
|
||||
|
||||
state.transform[0] = clampedX;
|
||||
state.transform[1] = clampedY;
|
||||
state.transform[2] = clampedZoom;
|
||||
state.d3Zoom = d3ZoomInstance;
|
||||
state.d3Selection = selection;
|
||||
state.d3ZoomHandler = defaultHandler;
|
||||
state.d3Initialised = true;
|
||||
}),
|
||||
|
||||
@@ -416,8 +423,9 @@ export const storeModel: StoreModel = {
|
||||
state.connectionPosition = position;
|
||||
}),
|
||||
|
||||
setConnectionNodeId: action((state, { connectionNodeId, connectionHandleType }) => {
|
||||
setConnectionNodeId: action((state, { connectionNodeId, connectionHandleId, connectionHandleType }) => {
|
||||
state.connectionNodeId = connectionNodeId;
|
||||
state.connectionHandleId = connectionHandleId;
|
||||
state.connectionHandleType = connectionHandleType;
|
||||
}),
|
||||
|
||||
|
||||
@@ -60,6 +60,8 @@ export interface Edge {
|
||||
type?: string;
|
||||
source: ElementId;
|
||||
target: ElementId;
|
||||
sourceHandle?: ElementId | null;
|
||||
targetHandle?: ElementId | null;
|
||||
label?: string;
|
||||
labelStyle?: CSSProperties;
|
||||
labelShowBg?: boolean;
|
||||
@@ -243,6 +245,8 @@ export type OnLoadFunc = (params: OnLoadParams) => void;
|
||||
export interface Connection {
|
||||
source: ElementId | null;
|
||||
target: ElementId | null;
|
||||
sourceHandle: ElementId | null;
|
||||
targetHandle: ElementId | null;
|
||||
}
|
||||
|
||||
export enum ConnectionLineType {
|
||||
@@ -276,6 +280,7 @@ export type OnConnectEndFunc = (event: MouseEvent) => void;
|
||||
|
||||
export type SetConnectionId = {
|
||||
connectionNodeId: ElementId | null;
|
||||
connectionHandleId: ElementId | null;
|
||||
connectionHandleType: HandleType | null;
|
||||
};
|
||||
|
||||
@@ -290,7 +295,7 @@ export interface HandleProps {
|
||||
isConnectable?: boolean;
|
||||
onConnect?: OnConnectFunc;
|
||||
isValidConnection?: (connection: Connection) => boolean;
|
||||
id?: string;
|
||||
id?: ElementId;
|
||||
style?: CSSProperties;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
@@ -40,29 +40,39 @@ export const removeElements = (elementsToRemove: Elements, elements: Elements):
|
||||
});
|
||||
};
|
||||
|
||||
const getEdgeId = ({ source, target }: Connection): ElementId => `reactflow__edge-${source}-${target}`;
|
||||
const getEdgeId = ({ source, sourceHandle, target, targetHandle }: Connection): ElementId =>
|
||||
`reactflow__edge-${source}${sourceHandle}-${target}${targetHandle}`;
|
||||
|
||||
const connectionExists = (edge: Edge, elements: Elements) => {
|
||||
return elements.some(
|
||||
(el) =>
|
||||
isEdge(el) &&
|
||||
el.source === edge.source &&
|
||||
el.target === edge.target &&
|
||||
(el.sourceHandle === edge.sourceHandle || (!el.sourceHandle && !edge.sourceHandle)) &&
|
||||
(el.targetHandle === edge.targetHandle || (!el.targetHandle && !edge.targetHandle))
|
||||
);
|
||||
};
|
||||
|
||||
export const addEdge = (edgeParams: Edge | Connection, elements: Elements): Elements => {
|
||||
if (!edgeParams.source || !edgeParams.target) {
|
||||
throw new Error("Can't create edge. An edge needs a source and a target.");
|
||||
console.warn("Can't create edge. An edge needs a source and a target.");
|
||||
return elements;
|
||||
}
|
||||
|
||||
// make sure that there is node with the target and one with the source id
|
||||
[edgeParams.source, edgeParams.target].forEach((id) => {
|
||||
const nodeId = id.includes('__') ? id.split('__')[0] : id;
|
||||
if (!elements.find((e) => isNode(e) && e.id === nodeId)) {
|
||||
throw new Error(`Can't create edge. Node with id=${nodeId} does not exist.`);
|
||||
}
|
||||
});
|
||||
|
||||
let edge: Edge;
|
||||
if (isEdge(edgeParams)) {
|
||||
return elements.concat({ ...edgeParams });
|
||||
edge = { ...edgeParams };
|
||||
} else {
|
||||
edge = {
|
||||
...edgeParams,
|
||||
id: getEdgeId(edgeParams),
|
||||
} as Edge;
|
||||
}
|
||||
|
||||
const edge = {
|
||||
...edgeParams,
|
||||
id: getEdgeId(edgeParams),
|
||||
} as Edge;
|
||||
if (connectionExists(edge, elements)) {
|
||||
return elements;
|
||||
}
|
||||
|
||||
return elements.concat(edge);
|
||||
};
|
||||
@@ -112,6 +122,8 @@ export const parseElement = (element: Node | Edge): Node | Edge => {
|
||||
...element,
|
||||
source: element.source.toString(),
|
||||
target: element.target.toString(),
|
||||
sourceHandle: element.sourceHandle ? element.sourceHandle.toString() : null,
|
||||
targetHandle: element.targetHandle ? element.targetHandle.toString() : null,
|
||||
id: element.id.toString(),
|
||||
type: element.type || 'default',
|
||||
};
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
module.exports = {
|
||||
env: {
|
||||
browser: true,
|
||||
es6: true,
|
||||
},
|
||||
plugins: ['react'],
|
||||
globals: {
|
||||
graphql: false,
|
||||
},
|
||||
parserOptions: {
|
||||
sourceType: 'module',
|
||||
ecmaFeatures: {
|
||||
experimentalObjectRestSpread: true,
|
||||
jsx: true,
|
||||
},
|
||||
},
|
||||
extends: 'react-app',
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
node_modules/
|
||||
public
|
||||
.gatsby-context.js
|
||||
.DS_Store
|
||||
.intermediate-representation/
|
||||
.cache/
|
||||
yarn.lock
|
||||
*.log
|
||||
.vscode
|
||||
@@ -1 +0,0 @@
|
||||
v10
|
||||
@@ -1 +0,0 @@
|
||||
src/pages/blog/**/*.md
|
||||
@@ -1,5 +0,0 @@
|
||||
{
|
||||
"trailingComma": "es5",
|
||||
"semi": true,
|
||||
"singleQuote": true
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
# React Flow Website
|
||||
|
||||

|
||||
|
||||
The [react flow website](https://reactflow.dev) powered by Gatsby
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
import 'babel-polyfill';
|
||||
import './src/styles/global.css';
|
||||
|
||||
export const onRouteUpdate = () => {
|
||||
document.body.classList.remove('noscroll');
|
||||
};
|
||||
@@ -1,101 +0,0 @@
|
||||
module.exports = {
|
||||
siteMetadata: {
|
||||
title: `React Flow`,
|
||||
siteUrl: `https://reactflow.dev`,
|
||||
description:
|
||||
'React Flow is a highly customizable library for building interactive node-based editors, flow charts and diagrams.',
|
||||
},
|
||||
plugins: [
|
||||
{
|
||||
resolve: `gatsby-source-filesystem`,
|
||||
options: {
|
||||
name: 'pages',
|
||||
path: `${__dirname}/src/pages`,
|
||||
},
|
||||
},
|
||||
{
|
||||
resolve: `gatsby-source-filesystem`,
|
||||
options: {
|
||||
name: 'exampleflows',
|
||||
path: `${__dirname}/src/example-flows`,
|
||||
},
|
||||
},
|
||||
{
|
||||
resolve: `gatsby-source-filesystem`,
|
||||
options: {
|
||||
name: `markdown`,
|
||||
path: `${__dirname}/src/markdown`,
|
||||
},
|
||||
},
|
||||
{
|
||||
resolve: `gatsby-source-filesystem`,
|
||||
options: {
|
||||
name: `assets`,
|
||||
path: `${__dirname}/src/assets`,
|
||||
ignore: [`**/.*`], // ignore files starting with a dot
|
||||
},
|
||||
},
|
||||
{
|
||||
resolve: `gatsby-plugin-manifest`,
|
||||
options: {
|
||||
icon: `src/assets/images/react-flow-logo.svg`,
|
||||
name: `React Flow`,
|
||||
short_name: `react-flow`,
|
||||
start_url: `/`,
|
||||
background_color: `#f7f0eb`,
|
||||
theme_color: `#1A192B`,
|
||||
display: `standalone`,
|
||||
lang: 'en',
|
||||
},
|
||||
},
|
||||
`gatsby-plugin-sharp`,
|
||||
`gatsby-remark-images`,
|
||||
{
|
||||
resolve: `gatsby-transformer-remark`,
|
||||
options: {
|
||||
plugins: [
|
||||
{
|
||||
resolve: `gatsby-remark-images`,
|
||||
options: {
|
||||
linkImagesToOriginal: false,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
resolve: `gatsby-plugin-mdx`,
|
||||
options: {
|
||||
gatsbyRemarkPlugins: [
|
||||
{
|
||||
resolve: `gatsby-remark-images`,
|
||||
options: {
|
||||
maxWidth: 1200,
|
||||
showCaptions: true,
|
||||
quality: 85,
|
||||
backgroundColor: 'white',
|
||||
linkImagesToOriginal: false,
|
||||
},
|
||||
},
|
||||
'gatsby-remark-copy-linked-files',
|
||||
],
|
||||
remarkPlugins: [require('remark-unwrap-images')],
|
||||
extensions: [`.md`, `.mdx`],
|
||||
},
|
||||
},
|
||||
`gatsby-transformer-json`,
|
||||
`gatsby-plugin-react-helmet`,
|
||||
`gatsby-plugin-emotion`,
|
||||
{
|
||||
resolve: `gatsby-plugin-react-svg`,
|
||||
options: {
|
||||
rule: {
|
||||
include: /icons/,
|
||||
},
|
||||
},
|
||||
},
|
||||
`gatsby-transformer-sharp`,
|
||||
`gatsby-plugin-netlify`,
|
||||
`gatsby-transformer-javascript-frontmatter`,
|
||||
],
|
||||
};
|
||||
@@ -1,42 +0,0 @@
|
||||
const path = require('path');
|
||||
const { createFilePath } = require('gatsby-source-filesystem');
|
||||
|
||||
const docsGenerator = require('./generators/docs');
|
||||
const exampleGenerator = require('./generators/examples');
|
||||
|
||||
exports.createPages = ({ graphql, actions }) => {
|
||||
const { createPage } = actions;
|
||||
|
||||
const generateDocs = docsGenerator(createPage, graphql);
|
||||
const generateExamples = exampleGenerator(createPage, graphql);
|
||||
|
||||
return Promise.all([generateDocs, generateExamples]);
|
||||
};
|
||||
|
||||
exports.onCreateNode = ({ node, actions, getNode }) => {
|
||||
const { createNodeField } = actions;
|
||||
|
||||
if (node.internal.type === 'Mdx') {
|
||||
const value = createFilePath({ node, getNode });
|
||||
|
||||
createNodeField({
|
||||
name: 'slug',
|
||||
node,
|
||||
value,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
exports.onCreateWebpackConfig = ({ stage, actions, loaders }) => {
|
||||
actions.setWebpackConfig({
|
||||
resolve: {
|
||||
modules: [path.resolve(__dirname, 'src'), 'node_modules'],
|
||||
alias: {
|
||||
'example-flows': path.resolve(__dirname, 'src', 'example-flows'),
|
||||
},
|
||||
},
|
||||
devServer: {
|
||||
host: '0.0.0.0',
|
||||
},
|
||||
});
|
||||
};
|
||||
@@ -1,66 +0,0 @@
|
||||
const path = require('path');
|
||||
|
||||
const docsTemplate = path.resolve('./src/templates/doc-page.js');
|
||||
|
||||
const docPagesQuery = `
|
||||
{
|
||||
docs: allMdx(
|
||||
filter: {
|
||||
fields: {
|
||||
slug: { regex: "/docs/" },
|
||||
}
|
||||
}
|
||||
) {
|
||||
edges {
|
||||
node {
|
||||
fields {
|
||||
slug
|
||||
}
|
||||
frontmatter {
|
||||
title
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const createDocPages = (createPage, docPages) => {
|
||||
const menu = docPages.map((page) => ({
|
||||
slug: page.node.fields.slug,
|
||||
title: page.node.frontmatter.title,
|
||||
}));
|
||||
|
||||
docPages.forEach((page) => {
|
||||
createPage({
|
||||
path: page.node.fields.slug,
|
||||
component: docsTemplate,
|
||||
context: {
|
||||
slug: page.node.fields.slug,
|
||||
menu,
|
||||
},
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const blogGenerator = (createPage, graphql) =>
|
||||
new Promise((resolve, reject) => {
|
||||
resolve(
|
||||
graphql(docPagesQuery).then((result) => {
|
||||
if (result.errors) {
|
||||
console.log(result.errors);
|
||||
reject(result.errors);
|
||||
}
|
||||
|
||||
const docPages = result.data.docs.edges;
|
||||
|
||||
try {
|
||||
createDocPages(createPage, docPages);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
module.exports = blogGenerator;
|
||||
@@ -1,55 +0,0 @@
|
||||
const path = require('path');
|
||||
|
||||
const exampleTemplate = path.resolve('./src/templates/example-page.js');
|
||||
|
||||
const examplePagesQuery = `{
|
||||
allExamplesJson {
|
||||
edges {
|
||||
node {
|
||||
slug
|
||||
title
|
||||
source
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const createExamplePage = (createPage, examplePages) => {
|
||||
examplePages.forEach((page) => {
|
||||
const { slug, title, source } = page.node;
|
||||
|
||||
createPage({
|
||||
path: slug,
|
||||
component: exampleTemplate,
|
||||
context: {
|
||||
slug: slug,
|
||||
title: title,
|
||||
source: source,
|
||||
sourceSlug: `/example-flows/${source}/`,
|
||||
},
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const exampleGenerator = (createPage, graphql) =>
|
||||
new Promise((resolve, reject) => {
|
||||
resolve(
|
||||
graphql(examplePagesQuery).then((result) => {
|
||||
if (result.errors) {
|
||||
console.log(result.errors);
|
||||
reject(result.errors);
|
||||
}
|
||||
|
||||
const pages = result.data.allExamplesJson.edges;
|
||||
|
||||
try {
|
||||
createExamplePage(createPage, pages);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
module.exports = exampleGenerator;
|
||||
@@ -1,63 +0,0 @@
|
||||
const path = require('path');
|
||||
const utils = require('./utils');
|
||||
|
||||
const defaultTemplate = path.resolve('./src/templates/default-page.js');
|
||||
|
||||
const mdQuery = `
|
||||
{
|
||||
pages: allMdx {
|
||||
edges {
|
||||
node {
|
||||
fields {
|
||||
slug
|
||||
}
|
||||
frontmatter {
|
||||
published
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const filterSpecialPages = (mdPage) => {
|
||||
const slug = mdPage.node.fields.slug;
|
||||
return slug.indexOf('/blog') !== 0 && slug.indexOf('/projects') !== 0;
|
||||
};
|
||||
|
||||
const createMdPages = (createPage, mdPages) => {
|
||||
mdPages
|
||||
.filter(filterSpecialPages)
|
||||
.filter(utils.filterPublished)
|
||||
.forEach((mdPage) => {
|
||||
createPage({
|
||||
path: mdPage.node.fields.slug,
|
||||
component: defaultTemplate,
|
||||
context: {
|
||||
slug: mdPage.node.fields.slug,
|
||||
fields: mdPage.node.fields,
|
||||
},
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const mdPagesGenerator = (createPage, graphql) =>
|
||||
new Promise((resolve, reject) => {
|
||||
resolve(
|
||||
graphql(mdQuery).then((result) => {
|
||||
if (result.errors) {
|
||||
console.log(result.errors);
|
||||
reject(result.errors);
|
||||
}
|
||||
|
||||
const mdPages = result.data.pages.edges;
|
||||
try {
|
||||
createMdPages(createPage, mdPages);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
module.exports = mdPagesGenerator;
|
||||
@@ -1,8 +0,0 @@
|
||||
function filterPublished(post) {
|
||||
const isPublished = post.node.frontmatter && post.node.frontmatter.published;
|
||||
return process.env.NODE_ENV === 'development' || isPublished;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
filterPublished,
|
||||
};
|
||||
@@ -1,2 +0,0 @@
|
||||
[[plugins]]
|
||||
package = "netlify-plugin-gatsby-cache"
|
||||
@@ -1,30 +0,0 @@
|
||||
curl -o ../static/fonts/NTDapper-thin.woff2 https://nodotypefoundry.com/fonts/ZDJ0TVNqY3pRa3BxYjBoWWRHVlRja0oxY1ZwUmVEWjVlR1o1YzNGVGRqQmlSQzlYUVhGd05HeEtSSEJxYmpVNGNtMWxZMVpyVDBaVmNIUk5LelpOVW04eWVrWlNZamR3TldWNGJrUnpjMjVKUkRGV1NGRTlQVG82Z3FzV3lOQ2w5eTVYY2c3NDExV1RSVG82TWpVME1RPT0=.woff2
|
||||
curl -o ../static/fonts/NTDapper-thin.woff https://nodotypefoundry.com/fonts/Vm10cVR6Z3hXbUZRYTNkTmQydHdiazhyYnpSTGNHcHlZbEZRV1ZveVFuQXdkRXhSYnpsTFRVeFJaVFYzVHpCVk9XOVpTbWN5TTBOUlN6Sm9ZbWR6V213NVp6Qm9TRGRPUnpGaU9YbE1ha1YyUzFkM00wRTlQVG82TmVMYXF3TGZBQnlVOHJ4QXY4cFdIem82TWpVME1RPT0=.woff
|
||||
curl -o ../static/fonts/NTDapper-thin.eot https://nodotypefoundry.com/fonts/UkdvMlNVbG5WQ3RHUVd0aVlWRjJNMmRtTm5ONFlWSklUR1ZYVUd3eFdtazJkRGRVVVZaR2IyTTVkbTVVYVZKeFVtd3lhbEJDTW1obFVWTk9kM1JQWTAxNGJVeGtUSFZ6TVZkM2FtWTFTa3AxVVZaelNFRTlQVG82a1o2V2pXL1hjdlhVRWFCN1lkbTUyam82TWpVME1RPT0=.eot
|
||||
|
||||
curl -o ../static/fonts/NTDapper-extralight.woff2 https://nodotypefoundry.com/fonts/TDNGeGFrSkRZbEp5ZDJnMVFscFNVM0pCTTFselZqTjNOVXRTV1hCMlJGVlpkVTl3UW1abGJVZFFiWFpWY1ZSblZFcEtkVWg1ZEc1bmRraEJhMnhvZEhGSmVUTnZOM1Z4TDJvclQycDZPVFJuZGpKbFFsRTlQVG82eFJlWVZWWUgrdC9uelFUSXJiaEhuVG82TWpVME1RPT0=.woff2
|
||||
curl -o ../static/fonts/NTDapper-extralight.woff https://nodotypefoundry.com/fonts/TmxSS2EyWTRlV2xvTmxwbmVYVnlZVFE0WnpNeWJYTnNaalo1VnpCNWVHa3plbnBaVGtKbU5EQjBRMUp4TUVSeU1pdE5ZMmRuTkhRd2IzSlBlbkU0Y1VOWFkwVllVR2xNYUc5RlVtaEJZbFp1V0RkSlNXYzlQVG82REVDWWpKN3pEbEdvbzN6RnN4NFAyVG82TWpVME1RPT0=.woff
|
||||
curl -o ../static/fonts/NTDapper-extralight.eot https://nodotypefoundry.com/fonts/VW5FMU5uZHlZVzU2U0hWV1RqWm9NMjgyYjFWMlVqRkNURlF4YWxNd2IyVTRkRVEwZWs0MWQwYzNWMUJXTjFwU2RrMHhNRmx3TlZCYU9HMHZkbXR2UW5CWWEwMVRlbWhyUjBsSWRtVjJWSHBHSzBSNE5uYzlQVG82S2Z0SVRJQm5XMy9YOEt5VS94OEdnVG82TWpVME1RPT0=.eot
|
||||
|
||||
curl -o ../static/fonts/NTDapper-light.woff2 https://nodotypefoundry.com/fonts/VldoRlMwaFdWMnhMVlZJMU0wcHlZazFJTkZSdFZUbE1OSGwxT0VoTlJtazNXVkJxU2xwd1oxUnJibEJGVEd0T01FTnpRMEk1TjJsdVFqRllPR0o0VURCaWIyeG1WR3RoT1RKTVdrVnZiR0pDUVZBek1tYzlQVG82RWhKeFJXcVZLeTl1WUgva3pZZ2Yzam82TWpVME1RPT0=.woff2
|
||||
curl -o ../static/fonts/NTDapper-light.woff https://nodotypefoundry.com/fonts/WmxkQ1ptMHdTakZ4U1RWWVIxVXdSVWxOUnpWSmJXaElSVUV4YWs4eVFsaFBkamM1THk5d1NYWXZabkZ0UTNGR1RXUjRXbnBxVVhZNVMwMUNNVW93Tnk5M2VXWlRPVTB4WnpsTWVTOTBRV3hwTm01NVdFRTlQVG82UkltekRSdUN4aVlzZXh3dlJ5MW1yem82TWpVME1RPT0=.woff
|
||||
curl -o ../static/fonts/NTDapper-light.eot https://nodotypefoundry.com/fonts/VGpsTWFVTnBjMFJVY0dSNFZtWlFNSGxOTWxOdFRURm1jSGxpU0hKbFJuUlhlVGwwWmtOMWNHaFdkMGRTZWxGSlNFNXZjWFpJTVVvcldtSkVOelJSYlhadmNFdFVMMGdyU1Raek9FYzBZVmh3ZVVkMmEyYzlQVG82TVR0OXZieThhYlk0Z2VJdm5xeUViem82TWpVME1RPT0=.eot
|
||||
|
||||
curl -o ../static/fonts/NTDapper-regular.woff2 https://nodotypefoundry.com/fonts/ZW1kYU1VSndOVE5EWVVKa2RWbHZNRzF0U0dNd2JHbzFWbEJTY1VSWmMxTndTVEJxUWpaSWR6ZGpkM1V5TkROTE0xTXdOMFZ5VEdsTmJqSnFRMlpDVXpkblVEbFFjWFJLV2tWcmN6SnFWRU0zWWk5SE9IYzlQVG82dk1pSVFnYXBVS2kvNkp1TUVsYi9vVG82TWpVME1RPT0=.woff2
|
||||
curl -o ../static/fonts/NTDapper-regular.woff https://nodotypefoundry.com/fonts/Um1KM1VXaHZaMlZCWlhkbWFrRm9hMUY2VWtWVVIzQktaMG8yVUhnMVdESldOM1pMVURObk9VRnBjbEV5TVZwNFZ6UTRSMFUwU1VSTWFIQndTbGd6ZVRoamVIVlZWalZ4ZDB4bWREaHlUSGhCVUdob2RrRTlQVG82aEdJdWs3VFZtM0JSZWttbWFWQ28vVG82TWpVME1RPT0=.woff
|
||||
curl -o ../static/fonts/NTDapper-regular.eot https://nodotypefoundry.com/fonts/V1dZM01EUTBWWFJNWjJwaWVsTmhkazUxZUhnMVVGVjRPR2xFV1drMGQyaFVOazl0ZFVZNE9VMVNRVGczUjJaS01ISXhhbWxqY1hvMVFXWjJSa3htUTIxUVpWbEZOa1V5VlhZMU9GWnViRVJhTVZwTGVHYzlQVG82UXRFTlVkbmJaa0p0anZ6bFZyNUZaVG82TWpVME1RPT0=.eot
|
||||
|
||||
|
||||
curl -o ../static/fonts/NTDapper-medium.woff2 https://nodotypefoundry.com/fonts/ZFdwd1NWbFdOVUZrWVRaSGVuWXZXVFJwU21KT1ZEVXJVRzB4T0ZvekswWk5VM2d6YTJZMWNHMW1jRTg0YzJrM2R6WnNWVTkxV21kaU1uQmpSQ3N6T1VzNVFsUktlbEJYWldrNWN6aFNhRFJIUVZSeFFrRTlQVG82V09yckl0STl1TDJIUmQxK0JtcGVzVG82TWpVME1RPT0=.woff2
|
||||
curl -o ../static/fonts/NTDapper-medium.woff https://nodotypefoundry.com/fonts/UjAxUFVIaG5lWGxtT0hWTWVYUmhRVkJWYVRCcVRqTkZPRTByTlZkTlpVdG9kMk5pTjJKaFNqWXJSbkJLUWxabGFHZ3ljbXRDVVVWSGJGZHVVVFZRTW1aeVJGWnlaa0V4WlhCTVVqaHlXV05GYUhWNlluYzlQVG82QWlEendjMjZRNDQ2R1BtVmJKbVRUem82TWpVME1RPT0=.woff
|
||||
curl -o ../static/fonts/NTDapper-medium.eot https://nodotypefoundry.com/fonts/T1d3d2FYaHlNWGs0ZFZGeGNXbElkVzlyYkhGUlNVVlBjVkozUVhsWWR6SlFOR00zYkVGQlNIWTROelExVGxwNVdVVlZXbEp6YzNoTFpVVXJSMlZ4Y1ZKMlF6RXpaWHBZVmxSM2JHNVZRa0ZwYVRKM04wRTlQVG82MUh0TW5nMG9qZHdhUzNsSmQ4YmZzVG82TWpVME1RPT0=.eot
|
||||
|
||||
|
||||
curl -o ../static/fonts/NTDapper-bold.woff2 https://nodotypefoundry.com/fonts/VHpsUVEyWlRlVmh5TWxsYU56aGxSMHNyVjFac1JYVlpValpvTlZOelFqbFVNRlpqUkhKUk0yaGhVRGRsYVd4VFJIRXlielk0UTNWemJWSmFiVzVEV2xoNGFrdHFXWE5JWmtkR2VVMXlNbkZNWTNSRFFYYzlQVG82ZzNjVW9hTDZpbE56ZGZIQ2l5cHdJem82TWpVME1RPT0=.woff2
|
||||
curl -o ../static/fonts/NTDapper-bold.woff https://nodotypefoundry.com/fonts/V0c5elowNDJORWd4WjBkS2VXNVpUVk5HTXpCUUwwVjVWMVJtY1V4TWEwdHVURFZCUVdobVVuQlpPR3BRYzJKYWVVMWxOMlpwTDA4MFlVRkpPRFl4Ykc1UVVsQjJiek5ZV1RCalptbDZTRFZTVURrd1FrRTlQVG82VGNHNzViL1VEOHdYVUU2dG1FdC9SVG82TWpVME1RPT0=.woff
|
||||
curl -o ../static/fonts/NTDapper-bold.eot https://nodotypefoundry.com/fonts/U1VFMlRYSTRhR2xXTlU1SGVESmplSFF4U1cxNGVXbERUV3BQVTJrMU5FczVVa1JGT0ZSdVVFMVVjVXBtTkc1SlQxZ3hiWEpqU1c5cE5VSlFRMVpWWm10ME9FeGlUMmx6VW1WbWFUQnlWblFyV2pSWE1sRTlQVG82c2UvSkEvTmdnQ0lTU3RqZ0MxOE8wRG82TWpVME1RPT0=.eot
|
||||
|
||||
|
||||
curl -o ../static/fonts/NTDapper-black.woff2 https://nodotypefoundry.com/fonts/VG1Vdk5rUnRNSEF2Y1VsTGFuQm9NQzluTTJsWWVVdHplaXRUUW1aYWRHVmpPRTQxU0hNemEzaGlTa05yYWtKaGJWVkdNVWxwUTFCYVJYRmFSMjA0YkZkTFRsQmFRWFI2YW5ad1ptdzVjQzlrZFhaQ1JsRTlQVG82UndqVFIxVmFLUEN0aGQvNDFLVnpqem82TWpVME1RPT0=.woff2
|
||||
curl -o ../static/fonts/NTDapper-black.woff https://nodotypefoundry.com/fonts/U3pOMVZsSm9VRTk2U2xWelRVWXlWVXhZTkVVemNGSlJWRXhxYkhWNlpVVTNSMlZrY0U0MlluUlBialJaUlZGRFZETXpZWFpFVDFSUFFXSnJZbFF3TW5oemVYUlFOVzFzVXpCS1YzQkNjWGRhWVVSeWJXYzlQVG82czRtQ2pqdmt5VkxWc1U0NzdOeGlWRG82TWpVME1RPT0=.woff
|
||||
curl -o ../static/fonts/NTDapper-black.eot https://nodotypefoundry.com/fonts/ZFU5NVdXWnVNMGRvYkhGTWFHOXNja2RVUzNoekwyVlFlSFpQUm5SUFIzVnhWMWhMYjBGT05IbDNLMXBsUlVsb09VOVVSMEpxTlRCSk5rcGFWMlZaT0dsTlFsTnRNREJGZHpCa1FtdFhPREZ3VUVkMU9GRTlQVG82eTROSXZ5ZDFXWGxiVkFUYkNmVE9TRG82TWpVME1RPT0=.eot
|
||||
@@ -1,71 +0,0 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { Octokit } = require('@octokit/rest');
|
||||
const octokit = new Octokit({
|
||||
auth: 'f5e6f805ea497290e162230f0e319ed7fde11cab',
|
||||
});
|
||||
|
||||
const outputPath = path.resolve(__dirname, '..', 'src/assets/data/github.json');
|
||||
|
||||
const getRepos = async () => {
|
||||
const repos = await octokit.repos.listForOrg({
|
||||
org: 'wbkd',
|
||||
per_page: 100,
|
||||
sort: 'created',
|
||||
});
|
||||
|
||||
return repos.data;
|
||||
};
|
||||
|
||||
const getData = async (repos) => {
|
||||
return Promise.all(
|
||||
repos.map(async (repo) => {
|
||||
const stats = await octokit.repos.getCommitActivityStats({
|
||||
owner: 'wbkd',
|
||||
repo: repo.name,
|
||||
});
|
||||
|
||||
return {
|
||||
repo: repo.name,
|
||||
stats: stats.data,
|
||||
};
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
const formatYearlyStats = (stats) => {
|
||||
return stats.reduce((year, current) => {
|
||||
return year.concat(current.days);
|
||||
}, []);
|
||||
};
|
||||
|
||||
const formatData = (data) => {
|
||||
return data
|
||||
.reduce((result, repoData) => {
|
||||
if (repoData.stats) {
|
||||
result.push(formatYearlyStats(repoData.stats));
|
||||
}
|
||||
|
||||
return result;
|
||||
}, [])
|
||||
.filter((values) => values.some((value) => value > 0))
|
||||
.map((values) =>
|
||||
values.reduce((result, value, i) => {
|
||||
if (value > 0) {
|
||||
result.push([i, value]);
|
||||
}
|
||||
return result;
|
||||
}, [])
|
||||
)
|
||||
.map((values) => ({ v: values }));
|
||||
};
|
||||
|
||||
(async () => {
|
||||
const repos = await getRepos();
|
||||
const data = await getData(repos);
|
||||
const outputData = formatData(data);
|
||||
|
||||
console.log(outputData);
|
||||
|
||||
fs.writeFileSync(outputPath, JSON.stringify(outputData));
|
||||
})();
|
||||
@@ -1,85 +0,0 @@
|
||||
{
|
||||
"name": "react-flow-website",
|
||||
"description": "",
|
||||
"version": "1.0.0",
|
||||
"author": "webkid",
|
||||
"bugs": {
|
||||
"url": "https://github.com/wbkd/react-flow/issues"
|
||||
},
|
||||
"browserslist": [
|
||||
">0.25%",
|
||||
"ie 11"
|
||||
],
|
||||
"dependencies": {
|
||||
"@emotion/core": "^10.0.35",
|
||||
"@emotion/styled": "^10.0.27",
|
||||
"@mdx-js/mdx": "^1.6.18",
|
||||
"@mdx-js/react": "^1.6.18",
|
||||
"@react-hook/size": "^2.1.1",
|
||||
"@svgr/webpack": "^5.4.0",
|
||||
"babel-polyfill": "^6.26.0",
|
||||
"d3-array": "^2.8.0",
|
||||
"d3-color": "^2.0.0",
|
||||
"d3-scale": "^3.2.3",
|
||||
"d3-shape": "^2.0.0",
|
||||
"emotion-normalize": "^10.1.0",
|
||||
"emotion-theming": "^10.0.27",
|
||||
"gatsby": "^2.24.73",
|
||||
"gatsby-image": "^2.4.21",
|
||||
"gatsby-plugin-draft": "^0.1.0",
|
||||
"gatsby-plugin-emotion": "^4.3.13",
|
||||
"gatsby-plugin-manifest": "^2.4.34",
|
||||
"gatsby-plugin-mdx": "^1.2.46",
|
||||
"gatsby-plugin-netlify": "^2.3.17",
|
||||
"gatsby-plugin-react-helmet": "^3.3.14",
|
||||
"gatsby-plugin-react-svg": "^3.0.0",
|
||||
"gatsby-plugin-svgr": "^2.0.2",
|
||||
"gatsby-remark-copy-linked-files": "^2.3.19",
|
||||
"gatsby-remark-images": "^3.3.33",
|
||||
"gatsby-remark-unwrap-images": "^1.0.2",
|
||||
"gatsby-source-filesystem": "^2.3.34",
|
||||
"gatsby-transformer-javascript-frontmatter": "^2.3.14",
|
||||
"gatsby-transformer-json": "^2.4.14",
|
||||
"gatsby-transformer-remark": "^2.8.38",
|
||||
"prism-react-renderer": "^1.1.1",
|
||||
"rc-table": "^7.10.2",
|
||||
"react": "^16.13.1",
|
||||
"react-dom": "^16.13.1",
|
||||
"react-flow-renderer": "^6.1.3",
|
||||
"react-helmet": "^6.1.0",
|
||||
"reflexbox": "^4.0.6",
|
||||
"remark-unwrap-images": "^2.0.0",
|
||||
"slugify": "^1.4.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@octokit/rest": "^18.0.6",
|
||||
"babel-preset-gatsby": "^0.5.13",
|
||||
"eslint": "^7.11.0",
|
||||
"eslint-plugin-react": "^7.21.4",
|
||||
"gatsby-cli": "^2.12.107",
|
||||
"gatsby-plugin-sharp": "^2.6.40",
|
||||
"gatsby-transformer-sharp": "^2.5.17",
|
||||
"gh-pages": "^3.1.0",
|
||||
"prettier": "^2.1.2"
|
||||
},
|
||||
"homepage": "https://reactflow.dev",
|
||||
"keywords": [
|
||||
"gatsby"
|
||||
],
|
||||
"license": "MIT",
|
||||
"main": "n/a",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/wbkd/react-flow.git"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "npm run dev",
|
||||
"dev": "gatsby develop -H 0.0.0.0",
|
||||
"lint": "eslint --ext .js,.jsx --ignore-pattern public .",
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"format": "prettier --trailing-comma es5 --no-semi --single-quote --write 'src/**/*.js' 'src/**/*.md'",
|
||||
"build": "GATSBY_EXPERIMENTAL_PAGE_BUILD_ON_DATA_CHANGES=true gatsby build --log-pages",
|
||||
"deploy": "gatsby build --prefix-paths && gh-pages -d public",
|
||||
"fix-semi": "eslint --quiet --ignore-pattern node_modules --ignore-pattern public --parser babel-eslint --no-eslintrc --rule '{\"semi\": [2, \"never\"], \"no-extra-semi\": [2]}' --fix gatsby-node.js"
|
||||
}
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
[
|
||||
{
|
||||
"title": "Overview",
|
||||
"slug": "examples/",
|
||||
"source": "Overview"
|
||||
},
|
||||
{
|
||||
"title": "Custom Node",
|
||||
"slug": "examples/custom-node/",
|
||||
"source": "CustomNode"
|
||||
},
|
||||
{
|
||||
"title": "Edges",
|
||||
"slug": "examples/edges/",
|
||||
"source": "Edges"
|
||||
},
|
||||
{
|
||||
"title": "Horizontal",
|
||||
"slug": "examples/horizontal/",
|
||||
"source": "Horizontal"
|
||||
},
|
||||
{
|
||||
"title": "Interaction",
|
||||
"slug": "examples/interaction/",
|
||||
"source": "Interaction"
|
||||
},
|
||||
{
|
||||
"title": "Provider",
|
||||
"slug": "examples/provider/",
|
||||
"source": "Provider"
|
||||
},
|
||||
{
|
||||
"title": "Stress",
|
||||
"slug": "examples/stress/",
|
||||
"source": "Stress"
|
||||
},
|
||||
{
|
||||
"title": "Validation",
|
||||
"slug": "examples/validation/",
|
||||
"source": "Validation"
|
||||
},
|
||||
{
|
||||
"title": "Custom Connectionline",
|
||||
"slug": "examples/custom-connectionline/",
|
||||
"source": "CustomConnectionLine"
|
||||
},
|
||||
{
|
||||
"title": "Edge Types",
|
||||
"slug": "examples/edge-types/",
|
||||
"source": "EdgeTypes"
|
||||
},
|
||||
{
|
||||
"title": "Hidden",
|
||||
"slug": "examples/hidden/",
|
||||
"source": "Hidden"
|
||||
}
|
||||
]
|
||||
@@ -1,7 +0,0 @@
|
||||
[
|
||||
{
|
||||
"title": "Circles - Visual degree planner",
|
||||
"url": "https://circles360.github.io/",
|
||||
"image": "../images/showcases/circles-showcase.png"
|
||||
}
|
||||
]
|
||||
@@ -1,3 +0,0 @@
|
||||
<svg width="23" height="15" viewBox="0 0 23 15" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path className="nostroke" fill-rule="evenodd" clip-rule="evenodd" d="M18.4535 6.49989L13.3195 1.73268L14.6805 0.26709L21.6805 6.76709L22.4696 7.49988L21.6805 8.23268L14.6805 14.7327L13.3195 13.2671L18.4535 8.49989L0 8.49988V6.49988L18.4535 6.49989Z" fill="#1A192B"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 371 B |
@@ -1 +0,0 @@
|
||||
<svg height="28" viewBox="0 0 21 21" width="28" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd" stroke="#2a2e3b" stroke-linecap="round" stroke-linejoin="round" transform="translate(3 2)"><circle cx="8.5" cy="8.5" r="8"/><path d="m11.621 6.379v4.242h-4.242" transform="matrix(.70710678 .70710678 .70710678 -.70710678 -3.227683 7.792317)"/><path d="m8.5 4.5v8" transform="matrix(0 1 -1 0 17 0)"/></g></svg>
|
||||
|
Before Width: | Height: | Size: 426 B |
@@ -1,5 +0,0 @@
|
||||
<svg width="54" height="54" viewBox="0 0 54 54" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="21" y="21.4141" width="2" height="16.1754" transform="rotate(-45 21 21.4141)" class="nostroke" fill="white"/>
|
||||
<rect x="22" y="33" width="2" height="16.1754" transform="rotate(-135 22 33)" class="nostroke" fill="white"/>
|
||||
<circle cx="27" cy="27" r="26" stroke="white" stroke-width="2"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 397 B |
@@ -1,3 +0,0 @@
|
||||
<svg width="20" height="19" viewBox="0 0 20 19" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M3.48265 8.328C4.42132 8.328 4.85865 7.89067 4.79465 7.016L4.63465 4.504C4.61332 4.06667 4.66665 3.67733 4.79465 3.336C4.92265 2.99467 5.11465 2.70667 5.37065 2.472C5.63732 2.22667 5.95199 2.04 6.31465 1.912C6.68799 1.784 7.10399 1.72 7.56265 1.72H8.65065V3.56H7.83465C7.45065 3.56 7.15199 3.65067 6.93865 3.832C6.72532 4.01333 6.62932 4.30133 6.65065 4.696L6.81065 7.208C6.84265 7.80533 6.66665 8.28 6.28265 8.632C5.90932 8.984 5.40799 9.17067 4.77865 9.192V9.272C5.41865 9.29333 5.92532 9.49067 6.29865 9.864C6.67199 10.2267 6.84265 10.7067 6.81065 11.304L6.65065 13.8C6.62932 14.152 6.72532 14.4293 6.93865 14.632C7.16265 14.824 7.46132 14.92 7.83465 14.92H8.65065V16.776H7.56265C7.11465 16.776 6.70399 16.7067 6.33065 16.568C5.95732 16.44 5.63732 16.2533 5.37065 16.008C5.11465 15.7627 4.91732 15.464 4.77865 15.112C4.65065 14.76 4.60265 14.3653 4.63465 13.928L4.79465 11.496C4.82665 11.0587 4.73599 10.728 4.52265 10.504C4.31999 10.28 3.97332 10.168 3.48265 10.168H1.57865V8.328H3.48265ZM11.4036 16.776V14.92H12.2196C12.6036 14.92 12.9023 14.8293 13.1156 14.648C13.3396 14.4667 13.4409 14.184 13.4196 13.8L13.2596 11.288C13.2169 10.6907 13.3823 10.216 13.7556 9.864C14.1396 9.512 14.6463 9.32533 15.2756 9.304V9.224C14.6356 9.20267 14.1289 9.01067 13.7556 8.648C13.3823 8.27467 13.2169 7.78933 13.2596 7.192L13.4196 4.696C13.4409 4.33333 13.3396 4.056 13.1156 3.864C12.8916 3.66133 12.5929 3.56 12.2196 3.56H11.4036V1.72H12.5076C12.9556 1.72 13.3609 1.78933 13.7236 1.928C14.0969 2.056 14.4116 2.24267 14.6676 2.488C14.9343 2.73333 15.1316 3.032 15.2596 3.384C15.3983 3.72533 15.4516 4.11467 15.4196 4.552L15.2596 7C15.2276 7.43733 15.3129 7.768 15.5156 7.992C15.7289 8.20533 16.0809 8.312 16.5716 8.312H18.4756V10.152H16.5716C15.6329 10.152 15.1956 10.5947 15.2596 11.48L15.4196 13.976C15.4409 14.4027 15.3876 14.7867 15.2596 15.128C15.1316 15.48 14.9396 15.7787 14.6836 16.024C14.4276 16.2693 14.1129 16.456 13.7396 16.584C13.3663 16.712 12.9556 16.776 12.5076 16.776H11.4036Z" fill="white" class="nostroke" />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 2.1 KiB |
@@ -1,5 +0,0 @@
|
||||
<svg width="30" height="20" viewBox="0 0 30 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<circle cx="14.5158" cy="10.0006" r="5.38829" stroke="white" stroke-width="1.48148"/>
|
||||
<path d="M27.0214 8.5334C27.7216 9.40104 27.7216 10.599 27.0214 11.4666C25.8703 12.8929 24.0857 14.8567 21.8995 16.4583C19.7074 18.0644 17.1761 19.2593 14.5161 19.2593C11.8561 19.2593 9.32487 18.0644 7.13272 16.4583C4.9466 14.8567 3.16191 12.8929 2.01087 11.4666C1.31069 10.599 1.31069 9.40104 2.01087 8.5334C3.16191 7.10708 4.9466 5.1433 7.13272 3.54168C9.32487 1.93565 11.8561 0.740741 14.5161 0.740741C17.1761 0.740741 19.7074 1.93565 21.8995 3.54168C24.0857 5.1433 25.8703 7.10708 27.0214 8.5334Z" stroke="white" stroke-width="1.48148"/>
|
||||
<circle cx="14.4447" cy="9.99999" r="3.33333" fill="white" class="nostroke" />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 810 B |
@@ -1,9 +0,0 @@
|
||||
<svg width="342" height="342" viewBox="0 0 342 342" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<mask id="path-1-outside-1" maskUnits="userSpaceOnUse" x="6" y="40" width="330" height="271" fill="black">
|
||||
<rect fill="white" x="6" y="40" width="330" height="271"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M19 48L123.5 290L171 180L218.5 290L323 48H228L114 48H19Z"/>
|
||||
</mask>
|
||||
<path d="M123.5 290L116.155 293.171L123.5 310.18L130.844 293.171L123.5 290ZM19 48V40H6.83145L11.6555 51.1715L19 48ZM171 180L178.344 176.829L171 159.82L163.655 176.829L171 180ZM218.5 290L211.155 293.171L218.5 310.18L225.844 293.171L218.5 290ZM323 48L330.345 51.1715L335.169 40H323V48ZM228 48L228 56H228V48ZM114 48L114 40H114V48ZM130.844 286.828L26.3445 44.8285L11.6555 51.1715L116.155 293.171L130.844 286.828ZM163.655 176.829L116.155 286.828L130.844 293.171L178.344 183.171L163.655 176.829ZM225.844 286.828L178.344 176.829L163.655 183.171L211.155 293.171L225.844 286.828ZM315.656 44.8285L211.155 286.828L225.844 293.171L330.345 51.1715L315.656 44.8285ZM228 56H323V40H228V56ZM114 56L228 56L228 40L114 40L114 56ZM19 56H114V40H19V56Z" fill="white" mask="url(#path-1-outside-1)"/>
|
||||
<path d="M123.5 290L19 48L228 48L123.5 290Z" fill="#C5CBD2"/>
|
||||
<path d="M218.5 290L114 48L323 48L218.5 290Z" fill="#1A192B"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.3 KiB |
@@ -1,3 +0,0 @@
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path class="nostroke" d="M18.6586 5.10534C17.7643 3.53477 16.5514 2.29135 15.0194 1.37479C13.4871 0.458185 11.8144 0 9.99991 0C8.18569 0 6.51238 0.458325 4.98046 1.37479C3.4483 2.2913 2.23539 3.53477 1.34116 5.10534C0.447068 6.67587 0 8.39089 0 10.2504C0 12.484 0.635737 14.4926 1.90753 16.2766C3.17919 18.0607 4.82198 19.2952 6.83577 19.9803C7.07018 20.0249 7.24371 19.9936 7.35654 19.887C7.46941 19.7804 7.52578 19.6467 7.52578 19.4868C7.52578 19.4601 7.52354 19.2199 7.51921 18.766C7.51474 18.3122 7.51264 17.9162 7.51264 17.5783L7.21315 17.6315C7.0222 17.6673 6.78132 17.6825 6.49049 17.6782C6.19981 17.674 5.89804 17.6428 5.58559 17.5847C5.27302 17.5272 4.98228 17.3937 4.71317 17.1846C4.44419 16.9755 4.25324 16.7018 4.14036 16.3639L4.01016 16.0568C3.92337 15.8523 3.78674 15.6251 3.60008 15.3761C3.41342 15.1269 3.22466 14.958 3.03371 14.869L2.94254 14.8021C2.8818 14.7577 2.82543 14.704 2.7733 14.6418C2.72122 14.5796 2.68223 14.5173 2.65619 14.455C2.6301 14.3926 2.65172 14.3414 2.72127 14.3012C2.79081 14.261 2.9165 14.2416 3.09888 14.2416L3.35919 14.2814C3.53281 14.3171 3.74757 14.4236 4.00373 14.6017C4.25976 14.7796 4.47023 15.0109 4.63518 15.2956C4.83493 15.6605 5.07559 15.9385 5.35784 16.1299C5.63986 16.3212 5.92421 16.4167 6.21061 16.4167C6.49702 16.4167 6.74438 16.3945 6.95279 16.3502C7.16098 16.3057 7.35631 16.2388 7.53868 16.1499C7.61681 15.5535 7.82951 15.0953 8.17661 14.775C7.68189 14.7217 7.2371 14.6414 6.84202 14.5347C6.44717 14.4278 6.03914 14.2544 5.6182 14.0139C5.19704 13.7738 4.84766 13.4756 4.56997 13.1198C4.29223 12.7639 4.06429 12.2966 3.88648 11.7183C3.70857 11.1399 3.6196 10.4726 3.6196 9.71627C3.6196 8.63941 3.96255 7.72304 4.64832 6.96665C4.32707 6.15705 4.3574 5.24947 4.73939 4.244C4.99113 4.16382 5.36445 4.22399 5.85918 4.42412C6.354 4.62434 6.71628 4.79587 6.94641 4.93808C7.17653 5.08024 7.36092 5.20071 7.49983 5.29842C8.30727 5.06715 9.14052 4.95149 9.99982 4.95149C10.8591 4.95149 11.6925 5.06715 12.5 5.29842L12.9948 4.97823C13.3332 4.76459 13.7327 4.56881 14.1925 4.39083C14.6526 4.21295 15.0044 4.16396 15.2475 4.24414C15.638 5.24966 15.6728 6.15719 15.3515 6.96679C16.0372 7.72318 16.3803 8.63978 16.3803 9.71641C16.3803 10.4727 16.291 11.1421 16.1133 11.725C15.9355 12.308 15.7056 12.7749 15.4236 13.1265C15.1412 13.4781 14.7896 13.774 14.3687 14.0141C13.9476 14.2544 13.5395 14.4278 13.1446 14.5347C12.7496 14.6415 12.3048 14.7219 11.8101 14.7752C12.2613 15.1755 12.4869 15.8073 12.4869 16.6704V19.4864C12.4869 19.6464 12.5412 19.7799 12.6498 19.8867C12.7583 19.9932 12.9297 20.0246 13.1641 19.9799C15.1782 19.2949 16.8209 18.0603 18.0926 16.2762C19.364 14.4922 20 12.4837 20 10.25C19.9995 8.39075 19.5522 6.67587 18.6586 5.10534Z" fill="#F8F9F9"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 2.7 KiB |
@@ -1 +0,0 @@
|
||||
<svg height="28" viewBox="0 0 21 21" width="28" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd" stroke="#2a2e3b" stroke-linecap="round" stroke-linejoin="round" transform="translate(3 3)"><path d="m.5.5v12c0 1.1045695.8954305 2 2 2h11.5"/><path d="m3.5 8.5v3"/><path d="m7.5 5.5v6"/><path d="m11.5 2.5v9"/></g></svg>
|
||||
|
Before Width: | Height: | Size: 337 B |
@@ -1,7 +0,0 @@
|
||||
<svg height="28" viewBox="0 0 21 21" width="28" xmlns="http://www.w3.org/2000/svg">
|
||||
<g fill="none" fill-rule="evenodd">
|
||||
<circle cx="10.5" cy="10.5" r="8" stroke="#2a2e3b" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="m10.5 14.5v-4" stroke="#2a2e3b" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<circle cx="10.5" cy="7.5" fill="#2a2e3b" r="1" class="nostroke" />
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 405 B |
@@ -1 +0,0 @@
|
||||
<svg height="28" viewBox="0 0 21 21" width="28" xmlns="http://www.w3.org/2000/svg"><path d="m6.5 7.5h4l-6 9v-6.997l-4-.003 6-9z" fill="none" stroke="#2a2e3b" stroke-linecap="round" stroke-linejoin="round" transform="translate(5 2)"/></svg>
|
||||
|
Before Width: | Height: | Size: 239 B |
@@ -1 +0,0 @@
|
||||
<svg height="28" viewBox="0 0 21 21" width="28" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd" stroke="#2a2e3b" stroke-linecap="round" stroke-linejoin="round" transform="translate(3 4)"><path d="m4.17157288 4.87867966v-1.41421357c0-1.56209716 1.26632995-2.82842712 2.82842712-2.82842712s2.82842712 1.26632996 2.82842712 2.82842712v1.41421357m0 2.82842712v2.82842712c0 1.5620972-1.26632995 2.8284271-2.82842712 2.8284271s-2.82842712-1.2663299-2.82842712-2.8284271v-2.82842712" transform="matrix(.70710678 .70710678 -.70710678 .70710678 7 -2.899495)"/><path d="m4.5 9.5 5-5"/></g></svg>
|
||||
|
Before Width: | Height: | Size: 607 B |
@@ -1 +0,0 @@
|
||||
<svg height="28" viewBox="0 0 21 21" width="28" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd" stroke="#2a2e3b" stroke-linecap="round" stroke-linejoin="round" transform="matrix(0 -1 1 0 2.5 16.5)"><path d="m2 .5h8c1.1045695 0 2 .8954305 2 2v10c0 1.1045695-.8954305 2-2 2h-8c-1.1045695 0-2-.8954305-2-2v-10c0-1.1045695.8954305-2 2-2z"/><path d="m2 6 5 3 5-3" transform="matrix(0 1 -1 0 14.5 .5)"/></g></svg>
|
||||
|
Before Width: | Height: | Size: 429 B |
@@ -1,12 +0,0 @@
|
||||
<svg width="32" height="20" viewBox="0 0 32 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0)">
|
||||
<rect y="2" width="2" height="32" transform="rotate(-90 0 2)" fill="white" class="nostroke"/>
|
||||
<rect y="13" width="2" height="32" transform="rotate(-90 0 13)" fill="white" class="nostroke"/>
|
||||
<rect y="24" width="2" height="32" transform="rotate(-90 0 24)" fill="white" class="nostroke"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0">
|
||||
<rect width="32" height="24" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 515 B |
@@ -1 +0,0 @@
|
||||
<svg fill="currentColor" height="40px" width="40px" preserveAspectRatio="xMidYMid meet" viewBox="0 0 40 40" style="margin-right: 0.3em; vertical-align: sub;"><g><path d="m34.5 11.7l-3 3.1-6.3-6.3 3.1-3q0.5-0.5 1.2-0.5t1.1 0.5l3.9 3.9q0.5 0.4 0.5 1.1t-0.5 1.2z m-29.5 17.1l18.4-18.5 6.3 6.3-18.4 18.4h-6.3v-6.2z"></path></g></svg>
|
||||
|
Before Width: | Height: | Size: 329 B |
@@ -1,11 +0,0 @@
|
||||
<svg width="28" height="28" viewBox="0 0 22 17" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="6.21875" y="0.5" width="9.62081" height="15.3397" rx="1.13397" stroke="#F8F9F9"/>
|
||||
<mask id="path-2-inside-1" fill="white">
|
||||
<rect x="8.16992" width="5.7189" height="2.45096" rx="0.816985"/>
|
||||
</mask>
|
||||
<rect class="nostroke" x="8.16992" width="5.7189" height="2.45096" rx="0.816985" stroke="#F8F9F9" stroke-width="2" mask="url(#path-2-inside-1)"/>
|
||||
<path d="M3.89531 10.0226C3.70565 9.83295 3.5552 9.60779 3.45256 9.35999C3.34991 9.11218 3.29708 8.84659 3.29708 8.57837C3.29708 8.31015 3.34991 8.04455 3.45256 7.79675C3.5552 7.54895 3.70565 7.32379 3.89531 7.13413L4.34941 7.58823C4.21938 7.71826 4.11624 7.87262 4.04587 8.04251C3.9755 8.2124 3.93928 8.39448 3.93928 8.57837C3.93928 8.76225 3.9755 8.94434 4.04587 9.11423C4.11624 9.28412 4.21938 9.43848 4.34941 9.56851L3.89531 10.0226Z" class="nostroke" fill="#F8F9F9"/>
|
||||
<path d="M18.1047 7.13413C18.2944 7.32379 18.4448 7.54895 18.5474 7.79675C18.6501 8.04456 18.7029 8.31015 18.7029 8.57837C18.7029 8.84659 18.6501 9.11218 18.5474 9.35999C18.4448 9.60779 18.2944 9.83295 18.1047 10.0226L17.6506 9.56851C17.7806 9.43848 17.8838 9.28412 17.9541 9.11423C18.0245 8.94434 18.0607 8.76225 18.0607 8.57837C18.0607 8.39448 18.0245 8.2124 17.9541 8.04251C17.8838 7.87262 17.7806 7.71826 17.6506 7.58823L18.1047 7.13413Z" class="nostroke" fill="#F8F9F9"/>
|
||||
<path d="M2.84487 11.4232C2.47128 11.0496 2.17493 10.6061 1.97274 10.118C1.77056 9.62987 1.66649 9.1067 1.66649 8.57837C1.66649 8.05003 1.77056 7.52687 1.97274 7.03875C2.17493 6.55063 2.47128 6.10712 2.84487 5.73353L3.30299 6.19165C2.98956 6.50508 2.74094 6.87717 2.57131 7.28669C2.40169 7.6962 2.31438 8.13511 2.31438 8.57837C2.31438 9.02162 2.40169 9.46054 2.57131 9.87005C2.74094 10.2796 2.98956 10.6517 3.30299 10.9651L2.84487 11.4232Z" class="nostroke" fill="#F8F9F9"/>
|
||||
<path d="M19.1546 5.73353C19.5282 6.10712 19.8246 6.55063 20.0268 7.03875C20.229 7.52687 20.333 8.05003 20.333 8.57837C20.333 9.10671 20.229 9.62987 20.0268 10.118C19.8246 10.6061 19.5282 11.0496 19.1546 11.4232L18.6965 10.9651C19.0099 10.6517 19.2586 10.2796 19.4282 9.87005C19.5978 9.46054 19.6851 9.02162 19.6851 8.57837C19.6851 8.13511 19.5978 7.6962 19.4282 7.28669C19.2586 6.87717 19.0099 6.50508 18.6965 6.19165L19.1546 5.73353Z" class="nostroke" fill="#F8F9F9"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 2.3 KiB |
@@ -1,3 +0,0 @@
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path class="nostroke" d="M10 20C15.5224 20 20 15.5224 20 10C20 4.47755 15.5224 0 10 0C4.47755 0 0 4.47755 0 10C0 15.5224 4.47755 20 10 20ZM7.06122 7.43878C8.98367 8.4102 10.6082 8.35306 10.6082 8.35306C10.6082 8.35306 9.99184 6.20612 11.8959 5.2551C13.8 4.30408 15.1102 5.90816 15.1102 5.90816C15.1102 5.90816 15.4429 5.81633 15.6898 5.72653C15.9367 5.63469 16.2959 5.47347 16.2959 5.47347L15.7102 6.52041L16.6143 6.42449C16.6143 6.42449 16.502 6.58775 16.1408 6.91837C15.7796 7.24898 15.6306 7.42041 15.6306 7.42041C15.6306 7.42041 15.7592 10.0041 14.398 11.9939C13.0367 13.9837 11.2776 15.1776 8.72245 15.4286C6.16735 15.6796 4.50408 14.6449 4.50408 14.6449C4.50408 14.6449 5.62041 14.5816 6.33265 14.3082C7.0449 14.0367 8.06939 13.3184 8.06939 13.3184C8.06939 13.3184 6.61429 12.8694 6.0898 12.3673C5.56735 11.8633 5.43673 11.5653 5.43673 11.5653L6.87347 11.5469C6.87347 11.5469 5.36122 10.7449 4.93265 10.1102C4.50408 9.47551 4.44694 8.85918 4.44694 8.85918L5.55102 9.30612C5.55102 9.30612 4.63265 8.05714 4.50204 7.08571C4.37143 6.11429 4.66939 5.59184 4.66939 5.59184C4.66939 5.59184 5.13878 6.46735 7.06122 7.43878Z" fill="#F8F9F9"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.2 KiB |
@@ -1,3 +0,0 @@
|
||||
<svg width="32" height="32" viewBox="0 0 24 17" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M21 3.95833C21.4055 4.25085 21.4054 4.25096 21.4053 4.25107L21.4052 4.2513L21.4048 4.25178L21.4041 4.25279L21.4025 4.25504L21.3984 4.26043L21.3875 4.27484C21.3788 4.28605 21.3673 4.3005 21.3526 4.3182C21.3233 4.35358 21.2813 4.40201 21.2237 4.46358C21.1086 4.58667 20.9308 4.76278 20.6671 4.99275C20.4214 5.20711 20.2499 5.36805 20.1416 5.47352C20.1377 5.47735 20.1338 5.4811 20.1301 5.48478C20.1306 5.5866 20.1294 5.72037 20.1237 5.88107C20.1102 6.26173 20.0716 6.79576 19.9713 7.41567C19.7716 8.64957 19.322 10.2554 18.3096 11.6658C16.3297 14.424 13.7448 16.0968 10.0203 16.4455C8.16106 16.6196 6.62325 16.3481 5.54671 16.0309C5.00842 15.8723 4.58465 15.7021 4.29244 15.5697C4.14627 15.5035 4.03284 15.4467 3.95425 15.4055C3.91495 15.3848 3.88435 15.3681 3.8627 15.356L3.83697 15.3414L3.8292 15.3369L3.82659 15.3353L3.82561 15.3348L3.82521 15.3345C3.82502 15.3344 3.82485 15.3343 4.07984 14.9042L3.82485 15.3343L2.40719 14.4938L4.05275 14.405L4.05277 14.405L4.05279 14.405L4.05287 14.4049L4.05322 14.4049L4.0558 14.4048L4.0674 14.4041L4.11508 14.401C4.15723 14.3982 4.21932 14.3937 4.29724 14.3871C4.45332 14.374 4.67174 14.3528 4.9199 14.3207C5.42536 14.2553 6.01914 14.1487 6.46287 13.9863L6.464 13.9859C6.89448 13.8295 7.43562 13.5412 7.89953 13.2703C7.85761 13.2545 7.81506 13.2382 7.77205 13.2215C7.17842 12.9909 6.40256 12.6461 5.9584 12.2409L5.95694 12.2396C5.5711 11.8848 5.3218 11.5951 5.16494 11.3852C5.08652 11.2803 5.03129 11.1955 4.99372 11.1327C4.97494 11.1013 4.96059 11.0754 4.94997 11.0553C4.94466 11.0452 4.94029 11.0366 4.93677 11.0295L4.93212 11.0199L4.93025 11.0159L4.92942 11.0141L4.92904 11.0133L4.92885 11.0129C4.92876 11.0127 4.92867 11.0125 5.38293 10.8036L4.92867 11.0125L4.60692 10.313L5.37683 10.3036L5.58523 10.3011C5.40249 10.1789 5.21981 10.0495 5.04849 9.91658C4.75412 9.68824 4.46335 9.42707 4.27066 9.1551C3.9336 8.67939 3.74314 8.21163 3.63652 7.8621C3.58306 7.68685 3.55016 7.53962 3.53034 7.4335C3.52041 7.38038 3.51373 7.33737 3.50937 7.30608C3.50719 7.29042 3.5056 7.27768 3.50446 7.26805L3.5031 7.25595L3.50265 7.2517L3.50248 7.25002L3.50241 7.2493L3.50238 7.24897C3.50236 7.24881 3.50235 7.24866 4 7.20024L3.50235 7.24866L3.42388 6.44206L4.17997 6.73376L4.28567 6.77453C3.98088 6.22476 3.67168 5.54509 3.58189 4.90863C3.48133 4.19582 3.54424 3.63487 3.63951 3.24146C3.68699 3.04536 3.74219 2.89216 3.78819 2.78354C3.81118 2.72924 3.83188 2.68608 3.84816 2.65425C3.85631 2.63834 3.86335 2.62525 3.86904 2.61501L3.87651 2.60179L3.87943 2.59679L3.88067 2.59469L3.88123 2.59374L3.8815 2.59329C3.88163 2.59307 3.88176 2.59286 4.3108 2.84961L3.88176 2.59286L4.32416 1.85357L4.74423 2.60027C4.74429 2.60038 4.74436 2.60049 4.74443 2.6006C4.74637 2.60375 4.75108 2.61125 4.7589 2.62285C4.77452 2.64605 4.80263 2.68573 4.8459 2.7399C4.93239 2.84817 5.07993 3.01486 5.31034 3.2238C5.77094 3.64147 6.56536 4.23034 7.86957 4.85841C9.16 5.47985 10.3503 5.77208 11.2158 5.9088C11.524 5.95749 11.7905 5.98635 12.0047 6.00336C11.9711 5.6978 11.9568 5.30368 12.0148 4.87208C12.1495 3.8695 12.6784 2.67058 14.1927 1.94976C15.6848 1.23952 16.9676 1.48277 17.8654 1.90958C18.3085 2.12025 18.6564 2.37417 18.894 2.57523C18.9512 2.62369 19.0024 2.66935 19.0472 2.71094C19.201 2.66688 19.3873 2.61096 19.5429 2.55711C19.7035 2.50012 19.9055 2.41987 20.0718 2.35183C20.1544 2.31809 20.2264 2.28806 20.2777 2.2665L20.3376 2.24118L20.3535 2.23446L20.3574 2.23278L20.3583 2.23238L20.3585 2.2323L20.3585 2.23229L21 3.95833ZM21 3.95833L21.4055 4.25085L22.0561 3.34899L20.9497 3.46086L20.6668 3.48946M21 3.95833L20.6668 3.48946M20.6668 3.48946L20.9864 2.94512L21.7557 1.63462L20.3585 2.23228L20.6668 3.48946Z" stroke="#F8F9F9"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 3.7 KiB |
|
Before Width: | Height: | Size: 2.7 MiB |
@@ -1,18 +0,0 @@
|
||||
<svg width="600" height="128" viewBox="0 0 600 128" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0)">
|
||||
<path d="M55.1521 128L1.88195e-05 -9.6431e-06L110.304 0L55.1521 128Z" fill="#C5CBD2"/>
|
||||
<path d="M105.152 128L50 -9.6431e-06L160.304 0L105.152 128Z" fill="#F8F9F9"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M321.911 0H448.853L321.911 106.517V0Z" fill="#C5CBD2"/>
|
||||
<rect x="175" width="89.6584" height="128" fill="#F8F9F9"/>
|
||||
<rect x="472.82" width="67.4658" height="128" fill="#F8F9F9"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M264.215 51.021C278.187 51.021 289.514 39.5995 289.514 25.5105C289.514 11.4214 278.187 0 264.215 0C250.242 0 238.915 11.4214 238.915 25.5105C238.915 39.5995 250.242 51.021 264.215 51.021Z" fill="#F8F9F9"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M263.771 127.999C284.852 127.999 301.942 110.767 301.942 89.5095C301.942 68.2524 284.852 51.02 263.771 51.02C242.689 51.02 225.599 68.2524 225.599 89.5095C225.599 110.767 242.689 127.999 263.771 127.999Z" fill="#F8F9F9"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M536.291 128C571.346 128 599.762 99.3462 599.762 64C599.762 28.6538 571.346 0 536.291 0C501.237 0 472.82 28.6538 472.82 64C472.82 99.3462 501.237 128 536.291 128Z" fill="#F8F9F9"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M321.911 0L448.853 128H321.911V0Z" fill="#F8F9F9"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0">
|
||||
<rect width="600" height="128" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.5 KiB |
@@ -1,18 +0,0 @@
|
||||
<svg width="600" height="128" viewBox="0 0 600 128" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0)">
|
||||
<path d="M55.1521 128L1.88195e-05 -9.6431e-06L110.304 0L55.1521 128Z" fill="#53606C"/>
|
||||
<path d="M105.152 128L50 -9.6431e-06L160.304 0L105.152 128Z" fill="#1A192B"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M321.911 0H448.853L321.911 106.517V0Z" fill="#53606C"/>
|
||||
<rect x="175" width="89.6584" height="128" fill="#1A192B"/>
|
||||
<rect x="472.82" width="67.4658" height="128" fill="#1A192B"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M264.215 51.021C278.187 51.021 289.514 39.5995 289.514 25.5105C289.514 11.4214 278.187 0 264.215 0C250.242 0 238.915 11.4214 238.915 25.5105C238.915 39.5995 250.242 51.021 264.215 51.021Z" fill="#1A192B"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M263.771 127.999C284.852 127.999 301.942 110.767 301.942 89.5095C301.942 68.2524 284.852 51.02 263.771 51.02C242.689 51.02 225.599 68.2524 225.599 89.5095C225.599 110.767 242.689 127.999 263.771 127.999Z" fill="#1A192B"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M536.291 128C571.346 128 599.762 99.3462 599.762 64C599.762 28.6538 571.346 0 536.291 0C501.237 0 472.82 28.6538 472.82 64C472.82 99.3462 501.237 128 536.291 128Z" fill="#1A192B"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M321.911 0L448.853 128H321.911V0Z" fill="#1A192B"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0">
|
||||
<rect width="600" height="128" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.5 KiB |
@@ -1,22 +0,0 @@
|
||||
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="40" height="40" fill="white"/>
|
||||
<rect x="23" y="23" width="14" height="14" rx="2" fill="white" stroke="#1A192B" stroke-width="2"/>
|
||||
<rect x="23" y="3" width="14" height="14" rx="2" fill="white" stroke="#FF0072" stroke-width="2"/>
|
||||
<rect x="3" y="23" width="14" height="14" rx="2" fill="white" stroke="#1A192B" stroke-width="2"/>
|
||||
<rect x="3" y="3" width="14" height="14" rx="2" fill="white" stroke="#1A192B" stroke-width="2"/>
|
||||
<circle cx="17" cy="10" r="3" fill="white"/>
|
||||
<circle cx="23" cy="10" r="3" fill="white"/>
|
||||
<circle cx="30" cy="17" r="3" fill="white"/>
|
||||
<circle cx="30" cy="23" r="3" fill="white"/>
|
||||
<circle cx="17" cy="30" r="3" fill="white"/>
|
||||
<circle cx="23" cy="30" r="3" fill="white"/>
|
||||
<circle cx="30" cy="23" r="2" fill="#1A192B"/>
|
||||
<circle cx="17" cy="30" r="2" fill="#1A192B"/>
|
||||
<circle cx="23" cy="30" r="2" fill="#1A192B"/>
|
||||
<rect opacity="0.35" x="18" y="9.5" width="4" height="1" fill="#1A192B"/>
|
||||
<rect opacity="0.35" x="29.5" y="21.5" width="4" height="1" transform="rotate(-90 29.5 21.5)" fill="#1A192B"/>
|
||||
<rect opacity="0.35" x="18" y="29.5" width="4" height="1" fill="#1A192B"/>
|
||||
<circle cx="17" cy="10" r="2" fill="#1A192B"/>
|
||||
<circle cx="23" cy="10" r="2" fill="#FF0072"/>
|
||||
<circle cx="30" cy="17" r="2" fill="#FF0072"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 162 KiB |
@@ -1,88 +0,0 @@
|
||||
import React from 'react';
|
||||
import styled from '@emotion/styled';
|
||||
import { rgba } from 'utils/css-utils';
|
||||
import { Flex } from 'reflexbox';
|
||||
|
||||
import Icon from 'components/Icon';
|
||||
import { getThemeColor } from 'utils/css-utils';
|
||||
|
||||
const Button = styled.button`
|
||||
color: ${(p) => p.theme.colors[p.color || 'button']};
|
||||
border: ${(p) =>
|
||||
p.type === 'ghost'
|
||||
? '1px solid rgba(0,0,0,0)'
|
||||
: `1px solid ${p.theme.colors[p.color || 'button']}`};
|
||||
background: ${(p) =>
|
||||
p.active
|
||||
? rgba(p.color ? p.theme.colors[p.color] : p.theme.colors.button, 0.2)
|
||||
: 'none'};
|
||||
padding: ${(p) => (p.type === 'big' ? '12px 20px' : '8px 16px')};
|
||||
border-radius: 25px;
|
||||
outline: none;
|
||||
cursor: pointer;
|
||||
font-size: ${(p) => p.theme.fontSizesPx[1]};
|
||||
letter-spacing: 0.5px;
|
||||
line-height: 1;
|
||||
transition: 0.075s all ease-in-out;
|
||||
|
||||
svg {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
&:visited,
|
||||
&:focus,
|
||||
&:active {
|
||||
color: ${(p) => p.theme.colors[p.color || 'button']};
|
||||
}
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
color: ${getThemeColor('background')};
|
||||
background-color: ${getThemeColor('button')};
|
||||
|
||||
svg {
|
||||
path,
|
||||
circle,
|
||||
rect,
|
||||
line,
|
||||
polyline {
|
||||
stroke: ${getThemeColor('background')};
|
||||
}
|
||||
}
|
||||
|
||||
svg {
|
||||
.nostroke {
|
||||
stroke: none;
|
||||
fill: ${getThemeColor('background')};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:active {
|
||||
transform: scale(0.95);
|
||||
}
|
||||
`;
|
||||
|
||||
export default ({
|
||||
icon,
|
||||
color = 'button',
|
||||
children,
|
||||
type = 'normal',
|
||||
iconWidth = '20px',
|
||||
...props
|
||||
}) => (
|
||||
<Button color={color} type={type} {...props}>
|
||||
<Flex alignItems="center" justifyContent="center">
|
||||
{icon && (
|
||||
<Icon
|
||||
strokeColor={color}
|
||||
name={icon}
|
||||
colorizeStroke
|
||||
width={iconWidth}
|
||||
mr={1}
|
||||
/>
|
||||
)}
|
||||
{children}
|
||||
</Flex>
|
||||
</Button>
|
||||
);
|
||||
@@ -1,13 +0,0 @@
|
||||
import styled from '@emotion/styled';
|
||||
import { Box } from 'reflexbox';
|
||||
|
||||
import { getThemeSpacePx } from 'utils/css-utils';
|
||||
|
||||
export default styled(Box)`
|
||||
max-width: ${(p) =>
|
||||
p.maxWidth || (p.big ? p.theme.maxWidthBig : p.theme.maxWidth)};
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
padding-left: ${getThemeSpacePx(3)};
|
||||
padding-right: ${getThemeSpacePx(3)};
|
||||
`;
|
||||
@@ -1,23 +0,0 @@
|
||||
import React from 'react';
|
||||
import styled from '@emotion/styled';
|
||||
import { Flex } from 'reflexbox';
|
||||
|
||||
import { getThemeSpacePx } from 'utils/css-utils';
|
||||
import Icon from 'components/Icon';
|
||||
|
||||
const Close = styled(Flex)`
|
||||
padding: ${getThemeSpacePx(1)};
|
||||
line-height: 1;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
`;
|
||||
|
||||
export default ({ onClick, ...props }) => {
|
||||
return (
|
||||
<Flex pb={4} alignItems="center" justifyContent="center" {...props}>
|
||||
<Close alignItems="center" justifyContent="center" onClick={onClick}>
|
||||
<Icon name="close" strokeColor="text" colorizeStroke />
|
||||
</Close>
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
@@ -1,45 +0,0 @@
|
||||
import React from 'react';
|
||||
import Highlight, { defaultProps } from 'prism-react-renderer';
|
||||
import dracula from 'prism-react-renderer/themes/github';
|
||||
|
||||
export default ({ children, className = 'javascript', language }) => {
|
||||
const lang = language ? language : className.replace(/language-/, '');
|
||||
|
||||
return (
|
||||
<Highlight
|
||||
{...defaultProps}
|
||||
theme={dracula}
|
||||
code={children}
|
||||
language={lang}
|
||||
>
|
||||
{({ className, style, tokens, getLineProps, getTokenProps }) => (
|
||||
<pre
|
||||
className={className}
|
||||
style={{
|
||||
...style,
|
||||
overflowX: 'auto',
|
||||
padding: 16,
|
||||
borderRadius: 4,
|
||||
margin: '20px 0 20px 0',
|
||||
fontSize: 14,
|
||||
}}
|
||||
>
|
||||
{tokens.map(
|
||||
(line, i, lines) =>
|
||||
!(
|
||||
line.length === 1 &&
|
||||
line[0].empty &&
|
||||
(i === 0 || i === lines.length - 1)
|
||||
) && (
|
||||
<div key={i} {...getLineProps({ line, key: i })}>
|
||||
{line.map((token, key) => (
|
||||
<span key={key} {...getTokenProps({ token, key })} />
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
)}
|
||||
</pre>
|
||||
)}
|
||||
</Highlight>
|
||||
);
|
||||
};
|
||||
@@ -1,41 +0,0 @@
|
||||
import React from 'react';
|
||||
import styled from '@emotion/styled';
|
||||
import { Box } from 'reflexbox';
|
||||
|
||||
import { colors } from 'themes';
|
||||
import CodeBlock from './Mdx';
|
||||
import { H4, AttributionText } from 'components/Typo';
|
||||
|
||||
const Wrapper = styled(Box)`
|
||||
margin: 0 auto;
|
||||
padding: 5px 0;
|
||||
|
||||
h4 {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
pre {
|
||||
margin: 0;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
code {
|
||||
border: 1px solid ${colors.lightGrey};
|
||||
border-radius: 3px;
|
||||
}
|
||||
`;
|
||||
|
||||
export default ({
|
||||
language = 'javascript',
|
||||
code,
|
||||
title = '',
|
||||
subtitle = '',
|
||||
}) => {
|
||||
return (
|
||||
<Wrapper>
|
||||
{title && <H4>{title}</H4>}
|
||||
<CodeBlock language={language}>{code}</CodeBlock>
|
||||
{subtitle && <AttributionText>{subtitle}</AttributionText>}
|
||||
</Wrapper>
|
||||
);
|
||||
};
|
||||
@@ -1,22 +0,0 @@
|
||||
import React, { Fragment } from 'react';
|
||||
import { Box } from 'reflexbox';
|
||||
|
||||
import CenterContent from 'components/CenterContent';
|
||||
|
||||
export default ({
|
||||
children,
|
||||
bg = 'transparent',
|
||||
centered = false,
|
||||
id = null,
|
||||
big = false,
|
||||
...rest
|
||||
}) => {
|
||||
const WrapperComponent = centered ? CenterContent : Fragment;
|
||||
const wrapperProps = centered ? { big: big } : {};
|
||||
|
||||
return (
|
||||
<Box id={id} bg={bg} py={[6, 7]} {...rest}>
|
||||
<WrapperComponent {...wrapperProps}>{children}</WrapperComponent>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
@@ -1,48 +0,0 @@
|
||||
import React from 'react';
|
||||
import styled from '@emotion/styled';
|
||||
import { Flex } from 'reflexbox';
|
||||
|
||||
import Icon from 'components/Icon';
|
||||
import Logo from 'components/Logo';
|
||||
|
||||
const Wrapper = styled.footer`
|
||||
flex-shrink: 0;
|
||||
border-top: ${(p) =>
|
||||
p.hasBorder ? `1px solid ${p.theme.colors.silverLighten30}` : 'nonde'};
|
||||
`;
|
||||
|
||||
const Header = ({ hasBorder = false }) => {
|
||||
return (
|
||||
<Wrapper hasBorder={hasBorder}>
|
||||
<Flex p={4}>
|
||||
<Flex alignItems="center">
|
||||
A project by
|
||||
<a href="https://webkid.io" style={{ marginLeft: 8 }}>
|
||||
<Logo />
|
||||
</a>
|
||||
</Flex>
|
||||
<Flex ml="auto">
|
||||
<Icon
|
||||
as="a"
|
||||
href="https://github.com/wbkd"
|
||||
name="github_circle"
|
||||
colorizeStroke
|
||||
strokeColor="text"
|
||||
style={{ marginRight: 8 }}
|
||||
width="24px"
|
||||
/>
|
||||
<Icon
|
||||
as="a"
|
||||
href="https://twitter.com/webk1d"
|
||||
name="twitter_circle"
|
||||
colorizeStroke
|
||||
strokeColor="text"
|
||||
width="24px"
|
||||
/>
|
||||
</Flex>
|
||||
</Flex>
|
||||
</Wrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export default Header;
|
||||
@@ -1,240 +0,0 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Link } from 'gatsby';
|
||||
import styled from '@emotion/styled';
|
||||
import { Flex, Box } from 'reflexbox';
|
||||
|
||||
import Icon from 'components/Icon';
|
||||
import Button from 'components/Button';
|
||||
import Close from 'components/Close';
|
||||
import { getThemeColor, getThemeSpacePx, device, px } from 'utils/css-utils';
|
||||
import useMenuHeight from 'hooks/useMenuHeight';
|
||||
|
||||
import ReactFlowLogo from 'assets/images/react-flow-logo.svg';
|
||||
|
||||
const Centered = styled(Flex)`
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 24px 16px;
|
||||
`;
|
||||
|
||||
const Wrapper = styled.nav`
|
||||
flex-shrink: 0;
|
||||
`;
|
||||
|
||||
const NavWrapper = styled(Box)`
|
||||
position: absolute;
|
||||
display: ${(p) => (p.menuOpen ? 'flex' : 'none')};
|
||||
height: ${(p) => px(p.height)};
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
padding: ${getThemeSpacePx(3)};
|
||||
z-index: 500;
|
||||
background: ${getThemeColor('background')};
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
|
||||
.desktop {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@media ${device.tablet} {
|
||||
display: block;
|
||||
height: auto;
|
||||
position: relative;
|
||||
padding: 0;
|
||||
margin-left: auto;
|
||||
width: auto;
|
||||
background: transparent;
|
||||
align-items: unset;
|
||||
flex-direction: row;
|
||||
|
||||
.desktop {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.mobile {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const Nav = styled.div`
|
||||
flex-grow: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
`;
|
||||
|
||||
const NavInner = styled.div`
|
||||
line-height: 1;
|
||||
width: 100%;
|
||||
|
||||
@media ${device.tablet} {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: row;
|
||||
width: auto;
|
||||
}
|
||||
`;
|
||||
|
||||
const NavItem = styled(Link)`
|
||||
text-decoration: none;
|
||||
font-size: 30px;
|
||||
display: block;
|
||||
padding: ${(p) => (p.isButton ? '8px 16px' : '16px 0')};
|
||||
text-align: center;
|
||||
color: ${getThemeColor('textLight')};
|
||||
|
||||
&:focus,
|
||||
&:visited,
|
||||
&:active {
|
||||
color: ${getThemeColor('textLight')};
|
||||
}
|
||||
|
||||
&.active {
|
||||
color: ${getThemeColor('text')};
|
||||
}
|
||||
|
||||
@media ${device.tablet} {
|
||||
margin-left: 50px;
|
||||
font-size: 16px;
|
||||
display: inline-block;
|
||||
padding: ${(p) => (p.isButton ? '8px 16px' : 0)};
|
||||
color: ${getThemeColor('textLight')};
|
||||
|
||||
&:focus,
|
||||
&:visited {
|
||||
color: ${getThemeColor('textLight')};
|
||||
}
|
||||
|
||||
&.active,
|
||||
&:hover {
|
||||
color: ${getThemeColor('text')};
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const GithubButton = styled.a`
|
||||
font-size: 30px;
|
||||
display: block;
|
||||
padding: ${(p) => (p.isButton ? '12px 24px' : '16px 0')} !important;
|
||||
text-align: center;
|
||||
color: ${getThemeColor('textLight')};
|
||||
|
||||
&:focus,
|
||||
&:visited,
|
||||
&:active {
|
||||
color: ${getThemeColor('textLight')};
|
||||
}
|
||||
|
||||
@media ${device.tablet} {
|
||||
margin-left: 50px;
|
||||
font-size: 16px;
|
||||
display: inline-block;
|
||||
background: ${(p) => p.theme.colors.textDark};
|
||||
|
||||
&&& {
|
||||
&:hover {
|
||||
background: ${getThemeColor('red')};
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const NavButton = styled.div`
|
||||
cursor: pointer;
|
||||
|
||||
@media ${device.tablet} {
|
||||
display: none;
|
||||
}
|
||||
`;
|
||||
|
||||
const Image = styled.img`
|
||||
display: block;
|
||||
width: 40px;
|
||||
margin-right: 12px;
|
||||
`;
|
||||
|
||||
const LogoTitle = styled(Box)`
|
||||
font-weight: 900;
|
||||
font-size: 24px;
|
||||
letter-spacing: 0.5px;
|
||||
line-height: 1.2;
|
||||
color: ${getThemeColor('violet')};
|
||||
`;
|
||||
|
||||
const LogoSubtitle = styled(Box)`
|
||||
font-size: 12px;
|
||||
letter-spacing: 0.5px;
|
||||
color: ${getThemeColor('silverDarken30')};
|
||||
line-height: 1.2;
|
||||
`;
|
||||
|
||||
const Header = () => {
|
||||
const [menuOpen, setMenuOpen] = useState(false);
|
||||
const menuHeight = useMenuHeight();
|
||||
|
||||
const toggleMenu = () => {
|
||||
const nextMenuOpen = !menuOpen;
|
||||
|
||||
document.body.classList.toggle('noscroll', nextMenuOpen);
|
||||
|
||||
setMenuOpen(nextMenuOpen);
|
||||
};
|
||||
|
||||
return (
|
||||
<Wrapper>
|
||||
<Centered>
|
||||
<Flex as={Link} to="/">
|
||||
<Image src={ReactFlowLogo} alt="React Flow Logo" />
|
||||
<Box>
|
||||
<LogoTitle>React Flow</LogoTitle>
|
||||
<LogoSubtitle>an open source library by webkid.io</LogoSubtitle>
|
||||
</Box>
|
||||
</Flex>
|
||||
<NavButton onClick={toggleMenu}>
|
||||
<Icon name="menu" strokeColor="text" colorizeStroke />
|
||||
</NavButton>
|
||||
<NavWrapper menuOpen={menuOpen} height={menuHeight}>
|
||||
<Nav>
|
||||
<NavInner>
|
||||
<NavItem to="/" activeClassName="active" className="mobile">
|
||||
Home
|
||||
</NavItem>
|
||||
<NavItem to="/docs/" activeClassName="active" partiallyActive>
|
||||
Docs
|
||||
</NavItem>
|
||||
<NavItem to="/examples/" activeClassName="active" partiallyActive>
|
||||
Examples
|
||||
</NavItem>
|
||||
|
||||
<Button
|
||||
as={GithubButton}
|
||||
href="https://github.com/wbkd/react-flow"
|
||||
activeClassName="active"
|
||||
icon="github_circle"
|
||||
className="desktop"
|
||||
isButton
|
||||
color="textInverted"
|
||||
>
|
||||
Github
|
||||
</Button>
|
||||
|
||||
<GithubButton
|
||||
href="https://github.com/wbkd/react-flow"
|
||||
activeClassName="active"
|
||||
className="mobile"
|
||||
isButton
|
||||
>
|
||||
Github
|
||||
</GithubButton>
|
||||
</NavInner>
|
||||
</Nav>
|
||||
<Close onClick={toggleMenu} className="mobile" />
|
||||
</NavWrapper>
|
||||
</Centered>
|
||||
</Wrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export default Header;
|
||||
@@ -1,72 +0,0 @@
|
||||
import React, { memo, useCallback } from 'react';
|
||||
import styled from '@emotion/styled';
|
||||
import { Handle } from 'react-flow-renderer';
|
||||
|
||||
import { getThemeColor } from 'utils/css-utils';
|
||||
|
||||
const ColorPickerNodeWrapper = styled.div`
|
||||
padding: 10px;
|
||||
background: white;
|
||||
border: 1px solid ${getThemeColor('violet')};
|
||||
border-radius: 4px;
|
||||
box-shadow: ${(p) =>
|
||||
p.selected ? `0 0 0 0.25px ${p.theme.colors.violet}` : 'none'};
|
||||
|
||||
.react-flow__handle {
|
||||
background: ${(p) => p.color};
|
||||
}
|
||||
|
||||
input {
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
width: 100%;
|
||||
height: 8px;
|
||||
background: ${getThemeColor('silver')};
|
||||
outline: none;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
input::-webkit-slider-thumb {
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
background: ${(p) => p.color};
|
||||
cursor: pointer;
|
||||
border-radius: 100%;
|
||||
}
|
||||
|
||||
input::-moz-range-thumb {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
background: ${(p) => p.color};
|
||||
cursor: pointer;
|
||||
border-radius: 100%;
|
||||
}
|
||||
`;
|
||||
|
||||
const ColorPickerNode = memo(({ data, id, selected }) => {
|
||||
const onChange = useCallback((event) => data.onChange(event, id), [data, id]);
|
||||
const colorName = `${data.color[0].toUpperCase()}${data.color.substr(1)}`;
|
||||
|
||||
return (
|
||||
<ColorPickerNodeWrapper
|
||||
color={data.color}
|
||||
value={data.value}
|
||||
selected={selected}
|
||||
>
|
||||
<div>{colorName} amount</div>
|
||||
<input
|
||||
type="range"
|
||||
min="0"
|
||||
max="255"
|
||||
value={data.value}
|
||||
onChange={onChange}
|
||||
className="nodrag"
|
||||
/>
|
||||
<Handle type="source" position="right" />
|
||||
</ColorPickerNodeWrapper>
|
||||
);
|
||||
});
|
||||
|
||||
export default ColorPickerNode;
|
||||
@@ -1,151 +0,0 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import ReactFlow, {
|
||||
ReactFlowProvider,
|
||||
Background,
|
||||
Controls,
|
||||
addEdge,
|
||||
} from 'react-flow-renderer';
|
||||
|
||||
import ColorPickerNode from './ColorPickerNode';
|
||||
|
||||
const windowWidth = typeof window !== 'undefined' ? window.innerWidth : 0;
|
||||
const hasTouch = typeof window !== 'undefined' && 'ontouchstart' in window;
|
||||
|
||||
const isSmallScreen = windowWidth < 800;
|
||||
|
||||
const getOffset = () => {
|
||||
if (isSmallScreen) {
|
||||
return 0;
|
||||
} else if (windowWidth < 1200) {
|
||||
return 425;
|
||||
} else {
|
||||
return (windowWidth - 1200) / 2 + 500;
|
||||
}
|
||||
};
|
||||
|
||||
const getColorNodeX = () => {
|
||||
if (windowWidth < 600) {
|
||||
return offsetLeft + 250;
|
||||
} else if (isSmallScreen) {
|
||||
return offsetLeft + 400;
|
||||
} else if (windowWidth < 1000) {
|
||||
return offsetLeft + 300;
|
||||
} else if (windowWidth < 1200) {
|
||||
return offsetLeft + 400;
|
||||
} else {
|
||||
return offsetLeft + 550;
|
||||
}
|
||||
};
|
||||
|
||||
const offsetLeft = getOffset();
|
||||
|
||||
const nodeTypes = {
|
||||
colorpicker: ColorPickerNode,
|
||||
};
|
||||
|
||||
const findNodeByColor = (color) => (n) =>
|
||||
n.type === 'colorpicker' && n.data.color === color;
|
||||
|
||||
export default () => {
|
||||
const [elements, setElements] = useState([]);
|
||||
const onConnect = (params) => setElements((els) => addEdge(params, els));
|
||||
|
||||
useEffect(() => {
|
||||
const onChange = (event, id) => {
|
||||
setElements((els) => {
|
||||
const nextElements = els.map((e) => {
|
||||
if (e.id !== id) {
|
||||
return e;
|
||||
}
|
||||
|
||||
const value = event.target.value;
|
||||
|
||||
return {
|
||||
...e,
|
||||
data: {
|
||||
...e.data,
|
||||
value,
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
const red = nextElements.find(findNodeByColor('red')).data.value;
|
||||
const green = nextElements.find(findNodeByColor('green')).data.value;
|
||||
const blue = nextElements.find(findNodeByColor('blue')).data.value;
|
||||
|
||||
const background = `rgb(${red}, ${green}, ${blue})`;
|
||||
const colorNode = nextElements.find((n) => n.id === '4');
|
||||
colorNode.style = {
|
||||
background,
|
||||
};
|
||||
colorNode.data = {
|
||||
...colorNode.data,
|
||||
label: background,
|
||||
};
|
||||
|
||||
return nextElements;
|
||||
});
|
||||
};
|
||||
|
||||
const initialElements = [
|
||||
{
|
||||
id: '1',
|
||||
type: 'colorpicker',
|
||||
data: { color: 'red', value: 105, onChange },
|
||||
sourcePosition: 'right',
|
||||
position: { x: offsetLeft + 50, y: isSmallScreen ? 250 : 5 },
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
type: 'colorpicker',
|
||||
data: { color: 'green', value: 100, onChange },
|
||||
sourcePosition: 'right',
|
||||
position: { x: offsetLeft, y: isSmallScreen ? 325 : 150 },
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
data: { color: 'blue', value: 165, onChange },
|
||||
type: 'colorpicker',
|
||||
sourcePosition: 'right',
|
||||
position: {
|
||||
x: isSmallScreen ? offsetLeft + 50 : offsetLeft + 120,
|
||||
y: isSmallScreen ? 400 : 300,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: '4',
|
||||
data: { label: 'rgb(105, 100, 165)' },
|
||||
targetPosition: 'left',
|
||||
sourcePosition: 'right',
|
||||
position: { x: getColorNodeX(), y: isSmallScreen ? 350 : 180 },
|
||||
style: {
|
||||
width: 200,
|
||||
background: 'rgb(105, 100, 165)',
|
||||
color: 'white',
|
||||
textShadow:
|
||||
'rgba(0, 0, 0, 0.4) 1px 0px 0px, rgba(0, 0, 0, 0.4) -1px 0px 0px, rgba(0, 0, 0, 0.4) 0px 1px 0px, rgba(0, 0, 0, 0.4) 0px -1px 0px',
|
||||
},
|
||||
},
|
||||
{ id: 'e1-4', source: '1', target: '4', animated: true },
|
||||
{ id: 'e2-4', source: '2', target: '4', animated: true },
|
||||
{ id: 'e3-4', source: '3', target: '4', animated: true },
|
||||
];
|
||||
|
||||
setElements(initialElements);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<ReactFlowProvider>
|
||||
<ReactFlow
|
||||
elements={elements}
|
||||
zoomOnScroll={false}
|
||||
nodeTypes={nodeTypes}
|
||||
onConnect={onConnect}
|
||||
paneMoveable={!hasTouch}
|
||||
>
|
||||
<Background />
|
||||
<Controls showInteractive={false} />
|
||||
</ReactFlow>
|
||||
</ReactFlowProvider>
|
||||
);
|
||||
};
|
||||
@@ -1,103 +0,0 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import styled from '@emotion/styled';
|
||||
import { Flex } from 'reflexbox';
|
||||
import { isOldIE } from 'utils/browser-utils';
|
||||
|
||||
import Mail from 'assets/icons/mail.svg';
|
||||
import Github from 'assets/icons/github_circle.svg';
|
||||
import ArrowRight from 'assets/icons/arrow_right.svg';
|
||||
import Menu from 'assets/icons/menu.svg';
|
||||
import Code from 'assets/icons/code.svg';
|
||||
|
||||
const IconWrapper = styled(Flex)`
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
svg {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
|
||||
path,
|
||||
circle,
|
||||
rect,
|
||||
line,
|
||||
polyline {
|
||||
stroke: ${(p) =>
|
||||
p.colorizeStroke
|
||||
? p.theme.colors[p.strokeColor] || p.theme.colors.text
|
||||
: 'none'};
|
||||
}
|
||||
|
||||
path.nostroke,
|
||||
circle.nostroke,
|
||||
rect.nostroke,
|
||||
line.nostroke,
|
||||
polyline.nostroke {
|
||||
stroke: none;
|
||||
fill: ${(p) =>
|
||||
p.colorizeStroke
|
||||
? p.theme.colors[p.strokeColor] || p.theme.colors.text
|
||||
: 'none'};
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const preLoaded = {
|
||||
mail: Mail,
|
||||
arrow_right: ArrowRight,
|
||||
menu: Menu,
|
||||
github_circle: Github,
|
||||
code: Code,
|
||||
};
|
||||
|
||||
const getPreLoadedIcon = (name) =>
|
||||
preLoaded[name] ? () => preLoaded[name] : null;
|
||||
|
||||
export default ({
|
||||
name = 'datavis',
|
||||
colorizeStroke = false,
|
||||
strokeColor = null,
|
||||
...restProps
|
||||
}) => {
|
||||
const [SVGComponent, setSVGComponent] = useState(getPreLoadedIcon(name));
|
||||
|
||||
useEffect(() => {
|
||||
if (!SVGComponent) {
|
||||
(async () => {
|
||||
try {
|
||||
const svgComp = await import(`assets/icons/${name}.svg`);
|
||||
setSVGComponent(svgComp);
|
||||
} catch (err) {
|
||||
console.warn(`error loading icon: ${name}`);
|
||||
}
|
||||
})();
|
||||
}
|
||||
}, [SVGComponent, name]);
|
||||
|
||||
if (!SVGComponent) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const IconComponent = SVGComponent.default
|
||||
? SVGComponent.default
|
||||
: SVGComponent;
|
||||
|
||||
if (isOldIE()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<IconWrapper
|
||||
className="icon"
|
||||
colorizeStroke={colorizeStroke}
|
||||
strokeColor={strokeColor}
|
||||
height={restProps.width || 'auto'}
|
||||
{...restProps}
|
||||
>
|
||||
<IconComponent />
|
||||
</IconWrapper>
|
||||
);
|
||||
};
|
||||
@@ -1,26 +0,0 @@
|
||||
import React from 'react';
|
||||
import styled from '@emotion/styled';
|
||||
import { Box } from 'reflexbox';
|
||||
|
||||
const Wrapper = styled(Box)`
|
||||
padding: 16px;
|
||||
background: ${(p) => p.theme.colors.violetLighten45};
|
||||
color: white;
|
||||
border-radius: 5px;
|
||||
margin: 20px 0;
|
||||
`;
|
||||
|
||||
const Title = styled(Box)`
|
||||
font-size: 20px;
|
||||
margin-bottom: 4px;
|
||||
font-weight: 900;
|
||||
`;
|
||||
|
||||
export default ({ title = null, text, ...rest }) => {
|
||||
return (
|
||||
<Wrapper {...rest}>
|
||||
<Title>{title}</Title>
|
||||
<div>{text}</div>
|
||||
</Wrapper>
|
||||
);
|
||||
};
|
||||
@@ -1,25 +0,0 @@
|
||||
import React from 'react';
|
||||
import { Link as GatsbyLink } from 'gatsby';
|
||||
|
||||
const Link = ({ children, to, activeClassName, partiallyActive, ...other }) => {
|
||||
const internal = /^\/(?!\/)/.test(to);
|
||||
|
||||
if (internal) {
|
||||
return (
|
||||
<GatsbyLink
|
||||
to={to}
|
||||
activeClassName={activeClassName}
|
||||
partiallyActive={partiallyActive}
|
||||
{...other}
|
||||
>
|
||||
{children}
|
||||
</GatsbyLink>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<a href={to} {...other}>
|
||||
{children}
|
||||
</a>
|
||||
);
|
||||
};
|
||||
export default Link;
|
||||
@@ -1,30 +0,0 @@
|
||||
import React from 'react';
|
||||
import styled from '@emotion/styled';
|
||||
import { useTheme } from 'emotion-theming';
|
||||
|
||||
import WbkdLogoWhite from 'assets/images/logo-white.svg';
|
||||
import WbkdLogoBlack from 'assets/images/logo.svg';
|
||||
|
||||
const Image = styled.img`
|
||||
display: block;
|
||||
width: 75px;
|
||||
`;
|
||||
|
||||
const getLogoSrc = (type, inverted, theme) => {
|
||||
if (type) {
|
||||
return type === 'white' ? WbkdLogoWhite : WbkdLogoBlack;
|
||||
}
|
||||
|
||||
if (inverted) {
|
||||
return theme.name === 'light' ? WbkdLogoWhite : WbkdLogoBlack;
|
||||
}
|
||||
|
||||
return theme.name === 'light' ? WbkdLogoBlack : WbkdLogoWhite;
|
||||
};
|
||||
|
||||
export default ({ type = null, inverted = false, style = {} }) => {
|
||||
const theme = useTheme();
|
||||
const logoSrc = getLogoSrc(type, inverted, theme);
|
||||
|
||||
return <Image src={logoSrc} alt="webkid logo" style={style} />;
|
||||
};
|
||||
@@ -1,26 +0,0 @@
|
||||
import React from 'react';
|
||||
import styled from '@emotion/styled';
|
||||
import { Flex, Box } from 'reflexbox';
|
||||
|
||||
import Page from 'components/Page';
|
||||
import Sidebar from 'components/Sidebar';
|
||||
|
||||
const Wrapper = styled(Flex)`
|
||||
border-top: 1px solid ${(p) => p.theme.colors.silverLighten30};
|
||||
`;
|
||||
|
||||
const DocWrapper = styled(Box)`
|
||||
max-width: 620px;
|
||||
margin: 0 auto;
|
||||
position: relative;
|
||||
padding: 0 16px;
|
||||
`;
|
||||
|
||||
export default ({ children, menu = [], ...rest }) => (
|
||||
<Page {...rest} footerBorder>
|
||||
<Wrapper>
|
||||
<Sidebar menu={menu} isDocs />
|
||||
<DocWrapper>{children}</DocWrapper>
|
||||
</Wrapper>
|
||||
</Page>
|
||||
);
|
||||
@@ -1,72 +0,0 @@
|
||||
import React from 'react';
|
||||
import styled from '@emotion/styled';
|
||||
import { Flex, Box } from 'reflexbox';
|
||||
|
||||
import Page from 'components/Page';
|
||||
import Sidebar from 'components/Sidebar';
|
||||
import useExamplePages from 'hooks/useExamplePages';
|
||||
import CodeBlock from 'components/CodeBlock';
|
||||
import { H4 } from 'components/Typo';
|
||||
|
||||
import 'example-flows/Overview';
|
||||
|
||||
const Wrapper = styled(Flex)`
|
||||
flex-grow: 1;
|
||||
`;
|
||||
|
||||
const ReactFlowWrapper = styled(Box)`
|
||||
flex-grow: 1;
|
||||
|
||||
.react-flow {
|
||||
border-bottom: 1px solid ${(p) => p.theme.colors.silverLighten30};
|
||||
height: 65vh;
|
||||
}
|
||||
`;
|
||||
|
||||
const SourceWrapper = styled(Box)`
|
||||
max-width: 1000px;
|
||||
margin: 0 auto;
|
||||
`;
|
||||
|
||||
const SourceCodeBlock = ({ absolutePath, internal }) => {
|
||||
const splittedPath = absolutePath.split('/');
|
||||
const fileName = splittedPath[splittedPath.length - 1];
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Box>{fileName}</Box>
|
||||
<CodeBlock code={internal.content} />
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default ({ children, title, slug, sourceCodeFiles = [] }) => {
|
||||
const menu = useExamplePages();
|
||||
|
||||
const metaTags = {
|
||||
title: `React Flow - ${title} Example`,
|
||||
siteUrl: `https://reactflow.dev/examples/${slug}`,
|
||||
robots: 'index, follow',
|
||||
};
|
||||
|
||||
const hasSource = sourceCodeFiles.length > 0;
|
||||
|
||||
return (
|
||||
<Page metaTags={metaTags} footerBorder>
|
||||
<Wrapper>
|
||||
<Sidebar menu={menu} />
|
||||
<ReactFlowWrapper>
|
||||
{children}
|
||||
{hasSource && (
|
||||
<SourceWrapper p={3}>
|
||||
<H4 as="div">{title} Source Code</H4>
|
||||
{sourceCodeFiles.map((source) => (
|
||||
<SourceCodeBlock key={source.absolutePath} {...source} />
|
||||
))}
|
||||
</SourceWrapper>
|
||||
)}
|
||||
</ReactFlowWrapper>
|
||||
</Wrapper>
|
||||
</Page>
|
||||
);
|
||||
};
|
||||
@@ -1,52 +0,0 @@
|
||||
import React from 'react';
|
||||
import { Helmet } from 'react-helmet';
|
||||
|
||||
const MetaTags = ({
|
||||
title,
|
||||
description,
|
||||
siteUrl,
|
||||
robots,
|
||||
image,
|
||||
pathname,
|
||||
article,
|
||||
}) => {
|
||||
if (pathname) {
|
||||
siteUrl = `${siteUrl}${pathname}`;
|
||||
}
|
||||
|
||||
if (image) {
|
||||
image = `https://reactflow.dev${image}`;
|
||||
} else {
|
||||
image = 'https://reactflow.dev/images/react-flow-header.jpg';
|
||||
}
|
||||
|
||||
return (
|
||||
<Helmet defaultTitle="React Flow">
|
||||
<html lang="en" />
|
||||
<title>{title}</title>
|
||||
{description && <meta name="description" content={description} />}
|
||||
{description && <meta name="robots" content={robots} />}
|
||||
|
||||
{siteUrl && <meta property="og:url" content={siteUrl} />}
|
||||
{(article ? true : null) && <meta property="og:type" content="article" />}
|
||||
{title && <meta property="og:title" content={title} />}
|
||||
{description && <meta property="og:description" content={description} />}
|
||||
{image && <meta property="og:image" content={image} />}
|
||||
|
||||
<meta name="twitter:card" content="summary_large_image" />
|
||||
{title && <meta name="twitter:title" content={title} />}
|
||||
{description && <meta name="twitter:description" content={description} />}
|
||||
{image && <meta name="twitter:image" content={image} />}
|
||||
|
||||
{process.env.NODE_ENV === 'production' && (
|
||||
<script
|
||||
src="https://cdn.usefathom.com/script.js"
|
||||
site="LXMRMWLB"
|
||||
defer
|
||||
></script>
|
||||
)}
|
||||
</Helmet>
|
||||
);
|
||||
};
|
||||
|
||||
export default MetaTags;
|
||||
@@ -1,51 +0,0 @@
|
||||
import React from 'react';
|
||||
import styled from '@emotion/styled';
|
||||
import { ThemeProvider } from 'emotion-theming';
|
||||
import { Flex, Box } from 'reflexbox';
|
||||
|
||||
import Header from 'components/Header';
|
||||
import Footer from 'components/Footer';
|
||||
import themes from 'themes';
|
||||
|
||||
import NormalizeStyle from 'themes/normalize';
|
||||
import GlobalStyle from 'themes/global';
|
||||
import MetaTags from './MetaTags';
|
||||
import { getThemeColor } from 'utils/css-utils';
|
||||
|
||||
const PageWrapper = styled(Flex)`
|
||||
color: ${getThemeColor('text')};
|
||||
width: 100%;
|
||||
position: relative;
|
||||
flex-direction: column;
|
||||
height: 100vh;
|
||||
position: relative;
|
||||
`;
|
||||
|
||||
const PageContent = styled(Box)`
|
||||
flex: 1 0 auto;
|
||||
`;
|
||||
|
||||
const Page = ({
|
||||
children,
|
||||
theme = 'light',
|
||||
metaTags,
|
||||
footerBorder = false,
|
||||
...rest
|
||||
}) => {
|
||||
const pageTheme = themes[theme];
|
||||
|
||||
return (
|
||||
<ThemeProvider theme={pageTheme}>
|
||||
<MetaTags {...metaTags} />
|
||||
<NormalizeStyle />
|
||||
<GlobalStyle />
|
||||
<PageWrapper>
|
||||
<Header />
|
||||
<PageContent {...rest}>{children}</PageContent>
|
||||
<Footer hasBorder={footerBorder} />
|
||||
</PageWrapper>
|
||||
</ThemeProvider>
|
||||
);
|
||||
};
|
||||
|
||||
export default Page;
|
||||
@@ -1,37 +0,0 @@
|
||||
import React from 'react';
|
||||
import styled from '@emotion/styled';
|
||||
import { Box } from 'reflexbox';
|
||||
|
||||
import { H1, H4 } from 'components/Typo';
|
||||
import { getThemeSpacePx, getThemeColor } from 'utils/css-utils';
|
||||
|
||||
const SectionIntroWrapper = styled(Box)`
|
||||
text-align: center;
|
||||
/* padding-top: ${getThemeSpacePx(5)};
|
||||
padding-bottom: ${getThemeSpacePx(
|
||||
6
|
||||
)}; */
|
||||
max-width: 700px;
|
||||
margin: 0 auto;
|
||||
|
||||
${H1} {
|
||||
margin: 0 0 ${getThemeSpacePx(3)} 0;
|
||||
}
|
||||
`;
|
||||
|
||||
const SectionSubtitle = styled(H4)`
|
||||
font-weight: 400;
|
||||
line-height: 1.5;
|
||||
color: ${getThemeColor('silverDarken30')};
|
||||
`;
|
||||
|
||||
const SectionIntro = ({ title = '', text = '', ...props }) => {
|
||||
return (
|
||||
<SectionIntroWrapper pt={[3, 3, 5]} pb={[4, 4, 6]} {...props}>
|
||||
<H1>{title}</H1>
|
||||
{text && <SectionSubtitle>{text}</SectionSubtitle>}
|
||||
</SectionIntroWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export default SectionIntro;
|
||||
@@ -1,92 +0,0 @@
|
||||
import React from 'react';
|
||||
import styled from '@emotion/styled';
|
||||
import { Flex, Box } from 'reflexbox';
|
||||
import Img from 'gatsby-image';
|
||||
|
||||
import CenterContent from 'components/CenterContent';
|
||||
import useShowcaseImages from 'hooks/useShowcaseImages';
|
||||
import { H4, Text } from 'components/Typo';
|
||||
import { getThemeColor } from 'utils/css-utils';
|
||||
import reactFlowIconSrc from 'assets/images/react-flow-logo.svg';
|
||||
|
||||
const gridPadding = 2;
|
||||
|
||||
const RoundImage = styled(Img)`
|
||||
border-radius: 4px;
|
||||
height: 250px;
|
||||
transition: transform 200ms ease;
|
||||
`;
|
||||
|
||||
const EmptyCase = styled(Box)`
|
||||
border-radius: 4px;
|
||||
height: 250px;
|
||||
background: ${(p) => p.theme.colors.silverLighten60};
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
`;
|
||||
|
||||
const Title = styled(H4)`
|
||||
color: ${getThemeColor('textInverted')};
|
||||
margin: 16px 0 0 0;
|
||||
font-weight: 400;
|
||||
`;
|
||||
|
||||
const Link = styled.a`
|
||||
&&& {
|
||||
color: ${getThemeColor('silverDarken15')};
|
||||
|
||||
&:hover {
|
||||
color: ${getThemeColor('silverLighten15')};
|
||||
|
||||
${RoundImage} {
|
||||
transform: scale(1.025);
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const Showcases = () => {
|
||||
const showcases = useShowcaseImages();
|
||||
const emptyShowCases = [...Array(3 - showcases.length).keys()];
|
||||
|
||||
return (
|
||||
<CenterContent>
|
||||
<Flex marginX={[0, 0, -gridPadding]} flexWrap="wrap">
|
||||
{showcases.map((showcase) => (
|
||||
<Box
|
||||
key={showcase.title}
|
||||
width={[1, 1, 1 / 3]}
|
||||
px={[0, 0, gridPadding]}
|
||||
mb={[3, 3, 0]}
|
||||
>
|
||||
<Link href={showcase.url} target="_blank" rel="noopener noreferrer">
|
||||
<RoundImage fluid={showcase.image.childImageSharp.fluid} />
|
||||
<Title>{showcase.title}</Title>
|
||||
|
||||
{showcase.url}
|
||||
</Link>
|
||||
</Box>
|
||||
))}
|
||||
{emptyShowCases.map((index) => (
|
||||
<Box
|
||||
key={index}
|
||||
width={[1, 1, 1 / 3]}
|
||||
px={[0, 0, gridPadding]}
|
||||
mb={[3, 3, 0]}
|
||||
>
|
||||
<EmptyCase>
|
||||
<img src={reactFlowIconSrc} alt="react flow logo" />
|
||||
</EmptyCase>
|
||||
<Title>Your Project here</Title>
|
||||
<Text color="silverDarken15">
|
||||
Let us know if you made something with React Flow.
|
||||
</Text>
|
||||
</Box>
|
||||
))}
|
||||
</Flex>
|
||||
</CenterContent>
|
||||
);
|
||||
};
|
||||
|
||||
export default Showcases;
|
||||
@@ -1,132 +0,0 @@
|
||||
import React, { Fragment, useState } from 'react';
|
||||
import Link from 'gatsby-link';
|
||||
import styled from '@emotion/styled';
|
||||
import { Flex } from 'reflexbox';
|
||||
|
||||
import Icon from 'components/Icon';
|
||||
import Close from 'components/Close';
|
||||
import useMenuHeight from 'hooks/useMenuHeight';
|
||||
import { getThemeColor, getThemeSpacePx, device, px } from 'utils/css-utils';
|
||||
|
||||
const Aside = styled.aside`
|
||||
display: ${(p) => (p.isOpen ? 'block' : 'none')};
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: ${(p) => px(p.height)};
|
||||
overflow-y: auto;
|
||||
background: white;
|
||||
z-index: 400;
|
||||
padding: 20px 10px;
|
||||
|
||||
@media ${device.tablet} {
|
||||
width: 30%;
|
||||
max-width: 300px;
|
||||
padding: 16px;
|
||||
border-right: 1px solid ${getThemeColor('silverLighten30')};
|
||||
display: block;
|
||||
position: relative;
|
||||
height: auto;
|
||||
|
||||
.mobile {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const AsideInner = styled.div``;
|
||||
|
||||
const MobileButton = styled(Flex)`
|
||||
z-index: 10;
|
||||
background: ${getThemeColor('violetLighten5')};
|
||||
position: fixed;
|
||||
right: 16px;
|
||||
bottom: 16px;
|
||||
color: white;
|
||||
border-radius: 100%;
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
cursor: pointer;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
@media ${device.tablet} {
|
||||
display: none;
|
||||
}
|
||||
`;
|
||||
|
||||
const MenuLink = styled(Link)`
|
||||
display: block;
|
||||
padding: ${getThemeSpacePx(1)} ${getThemeSpacePx(2)};
|
||||
margin-left: ${(p) => (p.marginLeft ? `${p.marginLeft}px` : 0)};
|
||||
|
||||
&.active,
|
||||
&:hover {
|
||||
background: ${getThemeColor('silverLighten30')};
|
||||
}
|
||||
`;
|
||||
|
||||
const GroupLabel = styled.div`
|
||||
padding: ${getThemeSpacePx(1)} ${getThemeSpacePx(2)};
|
||||
color: ${getThemeColor('silverDarken60')};
|
||||
margin-left: ${(p) => (p.marginLeft ? `${p.marginLeft}px` : 0)};
|
||||
`;
|
||||
|
||||
const MenuItem = ({ title, slug, marginLeft }) => {
|
||||
return (
|
||||
<MenuLink to={slug} marginLeft={marginLeft} activeClassName="active">
|
||||
{title}
|
||||
</MenuLink>
|
||||
);
|
||||
};
|
||||
|
||||
const SideBarParts = ({ items, level }) =>
|
||||
items.map((menuItem) => {
|
||||
if (menuItem.title) {
|
||||
return (
|
||||
<MenuItem key={menuItem.slug} marginLeft={level * 16} {...menuItem} />
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Fragment key={menuItem.group}>
|
||||
<GroupLabel marginLeft={level * 16}>{menuItem.group}</GroupLabel>
|
||||
<SideBarParts items={menuItem.items} level={level + 1} />
|
||||
</Fragment>
|
||||
);
|
||||
});
|
||||
|
||||
const Sidebar = ({ menu }) => {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const menuHeight = useMenuHeight();
|
||||
|
||||
const toggleSidebar = () => {
|
||||
const nextMenuOpen = !isOpen;
|
||||
|
||||
document.body.classList.toggle('noscroll', nextMenuOpen);
|
||||
|
||||
setIsOpen(nextMenuOpen);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<MobileButton onClick={toggleSidebar}>
|
||||
<Icon
|
||||
name="menu"
|
||||
width="24px"
|
||||
colorizeStroke
|
||||
strokeColor="silverLighten60"
|
||||
/>
|
||||
</MobileButton>
|
||||
<Aside isOpen={isOpen} height={menuHeight}>
|
||||
<AsideInner>
|
||||
<SideBarParts items={menu} level={0} />
|
||||
<Close className="mobile" onClick={toggleSidebar} />
|
||||
</AsideInner>
|
||||
</Aside>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default Sidebar;
|
||||
@@ -1,106 +0,0 @@
|
||||
import React, { useState } from 'react';
|
||||
import ReactFlow, {
|
||||
ReactFlowProvider,
|
||||
Background,
|
||||
Controls,
|
||||
addEdge,
|
||||
} from 'react-flow-renderer';
|
||||
|
||||
import TeaserFlow from 'components/TeaserFlow';
|
||||
import { baseColors } from 'themes';
|
||||
|
||||
const initialElements = [
|
||||
{
|
||||
id: '1',
|
||||
type: 'input',
|
||||
position: {
|
||||
x: 200,
|
||||
y: 5,
|
||||
},
|
||||
data: {
|
||||
label: 'Input',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
position: {
|
||||
x: 0,
|
||||
y: 150,
|
||||
},
|
||||
data: {
|
||||
label: 'Default',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
position: {
|
||||
x: 400,
|
||||
y: 150,
|
||||
},
|
||||
data: {
|
||||
label: 'Default',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: '4',
|
||||
type: 'output',
|
||||
position: {
|
||||
x: 200,
|
||||
y: 300,
|
||||
},
|
||||
data: {
|
||||
label: 'Output',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'e1',
|
||||
source: '1',
|
||||
target: '2',
|
||||
label: 'default edge',
|
||||
},
|
||||
{
|
||||
id: 'e2',
|
||||
source: '1',
|
||||
target: '3',
|
||||
animated: true,
|
||||
label: 'animated edge',
|
||||
},
|
||||
{
|
||||
id: 'e3',
|
||||
source: '2',
|
||||
target: '4',
|
||||
type: 'smoothstep',
|
||||
},
|
||||
{
|
||||
id: 'e4',
|
||||
source: '3',
|
||||
target: '4',
|
||||
type: 'smoothstep',
|
||||
},
|
||||
];
|
||||
|
||||
const onLoad = (rf) => rf.fitView({ padding: 0.2 });
|
||||
|
||||
export default () => {
|
||||
const [elements, setElements] = useState(initialElements);
|
||||
const onConnect = (params) => setElements((els) => addEdge(params, els));
|
||||
|
||||
return (
|
||||
<TeaserFlow
|
||||
title="Feature-rich"
|
||||
description="React Flow comes with seamless zooming & panning, different edge and node types, single and multi-selection, controls, several event handlers and more."
|
||||
>
|
||||
<ReactFlowProvider>
|
||||
<ReactFlow
|
||||
elements={elements}
|
||||
onLoad={onLoad}
|
||||
zoomOnScroll={false}
|
||||
onConnect={onConnect}
|
||||
>
|
||||
<Background color={baseColors.silverDarken60} gap={15} />
|
||||
<Controls showInteractive={false} />
|
||||
</ReactFlow>
|
||||
</ReactFlowProvider>
|
||||
</TeaserFlow>
|
||||
);
|
||||
};
|
||||
@@ -1,194 +0,0 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import styled from '@emotion/styled';
|
||||
import ReactFlow, {
|
||||
Handle,
|
||||
ReactFlowProvider,
|
||||
Background,
|
||||
Controls,
|
||||
} from 'react-flow-renderer';
|
||||
|
||||
import TeaserFlow from 'components/TeaserFlow';
|
||||
import { baseColors } from 'themes';
|
||||
|
||||
const NodeWrapper = styled.div`
|
||||
background: ${(p) => p.theme.colors.silverLighten30};
|
||||
padding: 8px 10px;
|
||||
font-size: 12px;
|
||||
border-radius: 4px;
|
||||
|
||||
input {
|
||||
font-size: 12px;
|
||||
border: 1px solid ${(p) => p.theme.colors.violetLighten60};
|
||||
width: 100%;
|
||||
border-radius: 2px;
|
||||
max-width: 100px;
|
||||
}
|
||||
|
||||
.react-flow__handle {
|
||||
background: ${(p) => p.theme.colors.violetLighten60};
|
||||
}
|
||||
`;
|
||||
|
||||
const InputLabel = styled.div`
|
||||
color: ${(p) => p.theme.colors.violetLighten60};
|
||||
`;
|
||||
|
||||
const InputNode = ({ id, data, isConnectable }) => {
|
||||
return (
|
||||
<NodeWrapper>
|
||||
<InputLabel>{data.label}:</InputLabel>
|
||||
<input
|
||||
className="nodrag"
|
||||
value={data.value}
|
||||
onChange={(event) => data.onChange(event, id)}
|
||||
/>
|
||||
<Handle type="source" position="bottom" isConnectable={isConnectable} />
|
||||
</NodeWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
const ResultNode = ({ data, isConnectable }) => {
|
||||
return (
|
||||
<NodeWrapper>
|
||||
<div>{data.value}</div>
|
||||
<Handle
|
||||
type="target"
|
||||
position="top"
|
||||
id="a"
|
||||
style={{ left: '40%' }}
|
||||
isConnectable={isConnectable}
|
||||
/>
|
||||
<Handle
|
||||
type="target"
|
||||
position="top"
|
||||
id="b"
|
||||
style={{ left: '60%' }}
|
||||
isConnectable={isConnectable}
|
||||
/>
|
||||
</NodeWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
const nodeTypes = {
|
||||
nameinput: InputNode,
|
||||
result: ResultNode,
|
||||
};
|
||||
|
||||
const onLoad = (rf) => setTimeout(() => rf.fitView({ padding: 0.1 }), 1);
|
||||
|
||||
const findNodeById = (id) => (n) => n.id === id;
|
||||
|
||||
export default () => {
|
||||
const [elements, setElements] = useState([]);
|
||||
|
||||
useEffect(() => {
|
||||
const onChange = (event, id) => {
|
||||
setElements((els) => {
|
||||
const nextElements = els.map((e) => {
|
||||
if (e.id !== id) {
|
||||
return e;
|
||||
}
|
||||
|
||||
const value = event.target.value;
|
||||
|
||||
return {
|
||||
...e,
|
||||
data: {
|
||||
...e.data,
|
||||
value,
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
const forname = nextElements.find(findNodeById('1')).data.value;
|
||||
const lastname = nextElements.find(findNodeById('2')).data.value;
|
||||
|
||||
const result = `${forname} ${lastname}`;
|
||||
const resultNode = nextElements.find((n) => n.type === 'result');
|
||||
|
||||
resultNode.data = {
|
||||
...resultNode.data,
|
||||
value: !forname && !lastname ? '*please enter a name*' : result,
|
||||
};
|
||||
|
||||
return nextElements;
|
||||
});
|
||||
};
|
||||
|
||||
const initialElements = [
|
||||
{
|
||||
id: '1',
|
||||
type: 'nameinput',
|
||||
position: {
|
||||
x: 0,
|
||||
y: 200,
|
||||
},
|
||||
data: {
|
||||
label: 'Forename',
|
||||
value: 'React',
|
||||
onChange,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
type: 'nameinput',
|
||||
position: {
|
||||
x: 400,
|
||||
y: 200,
|
||||
},
|
||||
data: {
|
||||
label: 'Lastname',
|
||||
value: 'Flow',
|
||||
onChange,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
type: 'result',
|
||||
position: {
|
||||
x: 200,
|
||||
y: 400,
|
||||
},
|
||||
data: {
|
||||
value: 'React Flow',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'e1',
|
||||
source: '1',
|
||||
target: '3__a',
|
||||
animated: true,
|
||||
},
|
||||
{
|
||||
id: 'e2',
|
||||
source: '2',
|
||||
target: '3__b',
|
||||
animated: true,
|
||||
},
|
||||
];
|
||||
|
||||
setElements(initialElements);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<TeaserFlow
|
||||
title="Customizable"
|
||||
description="You can create your own node and edge types or just pass a custom style. You can implement custom UIs inside your nodes and add functionality to your edges."
|
||||
textPosition="right"
|
||||
isDark
|
||||
>
|
||||
<ReactFlowProvider>
|
||||
<ReactFlow
|
||||
elements={elements}
|
||||
nodeTypes={nodeTypes}
|
||||
onLoad={onLoad}
|
||||
zoomOnScroll={false}
|
||||
nodesConnectable={false}
|
||||
>
|
||||
<Background color={baseColors.silverDarken60} gap={15} />
|
||||
<Controls showInteractive={false} />
|
||||
</ReactFlow>
|
||||
</ReactFlowProvider>
|
||||
</TeaserFlow>
|
||||
);
|
||||
};
|
||||
@@ -1,196 +0,0 @@
|
||||
import React, { useState } from 'react';
|
||||
import ReactFlow, {
|
||||
ReactFlowProvider,
|
||||
Background,
|
||||
Controls,
|
||||
addEdge,
|
||||
MiniMap,
|
||||
} from 'react-flow-renderer';
|
||||
|
||||
import TeaserFlow from 'components/TeaserFlow';
|
||||
|
||||
const defaultNodeOptions = {
|
||||
targetPosition: 'left',
|
||||
sourcePosition: 'right',
|
||||
style: {
|
||||
width: 50,
|
||||
},
|
||||
};
|
||||
|
||||
const initialElements = [
|
||||
{
|
||||
id: 'input',
|
||||
type: 'input',
|
||||
position: {
|
||||
x: 0,
|
||||
y: 100,
|
||||
},
|
||||
data: {
|
||||
label: 'Input',
|
||||
},
|
||||
...defaultNodeOptions,
|
||||
},
|
||||
{
|
||||
id: 'A',
|
||||
position: {
|
||||
x: 150,
|
||||
y: 0,
|
||||
},
|
||||
data: {
|
||||
label: 'A',
|
||||
},
|
||||
...defaultNodeOptions,
|
||||
},
|
||||
{
|
||||
id: 'B',
|
||||
position: {
|
||||
x: 250,
|
||||
y: 0,
|
||||
},
|
||||
data: {
|
||||
label: 'B',
|
||||
},
|
||||
...defaultNodeOptions,
|
||||
},
|
||||
{
|
||||
id: 'C',
|
||||
position: {
|
||||
x: 350,
|
||||
y: 0,
|
||||
},
|
||||
data: {
|
||||
label: 'C',
|
||||
},
|
||||
...defaultNodeOptions,
|
||||
},
|
||||
{
|
||||
id: 'D',
|
||||
position: {
|
||||
x: 150,
|
||||
y: 200,
|
||||
},
|
||||
data: {
|
||||
label: 'D',
|
||||
},
|
||||
...defaultNodeOptions,
|
||||
},
|
||||
{
|
||||
id: 'E',
|
||||
position: {
|
||||
x: 250,
|
||||
y: 200,
|
||||
},
|
||||
data: {
|
||||
label: 'E',
|
||||
},
|
||||
...defaultNodeOptions,
|
||||
},
|
||||
{
|
||||
id: 'F',
|
||||
position: {
|
||||
x: 350,
|
||||
y: 200,
|
||||
},
|
||||
data: {
|
||||
label: 'F',
|
||||
},
|
||||
...defaultNodeOptions,
|
||||
},
|
||||
{
|
||||
id: 'output',
|
||||
type: 'output',
|
||||
position: {
|
||||
x: 500,
|
||||
y: 100,
|
||||
},
|
||||
data: {
|
||||
label: 'Output',
|
||||
},
|
||||
...defaultNodeOptions,
|
||||
},
|
||||
{
|
||||
id: 'e1',
|
||||
source: 'input',
|
||||
target: 'A',
|
||||
type: 'step',
|
||||
},
|
||||
{
|
||||
id: 'e2',
|
||||
source: 'A',
|
||||
target: 'B',
|
||||
type: 'step',
|
||||
},
|
||||
{
|
||||
id: 'e3',
|
||||
source: 'B',
|
||||
target: 'C',
|
||||
type: 'step',
|
||||
},
|
||||
{
|
||||
id: 'e4',
|
||||
source: 'C',
|
||||
target: 'output',
|
||||
type: 'step',
|
||||
},
|
||||
{
|
||||
id: 'e5',
|
||||
source: 'input',
|
||||
target: 'D',
|
||||
type: 'step',
|
||||
animated: true,
|
||||
},
|
||||
{
|
||||
id: 'e6',
|
||||
source: 'D',
|
||||
target: 'E',
|
||||
type: 'step',
|
||||
animated: true,
|
||||
},
|
||||
{
|
||||
id: 'e7',
|
||||
source: 'E',
|
||||
target: 'F',
|
||||
type: 'step',
|
||||
animated: true,
|
||||
},
|
||||
{
|
||||
id: 'e8',
|
||||
source: 'F',
|
||||
target: 'output',
|
||||
type: 'step',
|
||||
animated: true,
|
||||
},
|
||||
];
|
||||
|
||||
const onLoad = (rf) => rf.fitView({ padding: 0.2 });
|
||||
|
||||
export default () => {
|
||||
const [elements, setElements] = useState(initialElements);
|
||||
const onConnect = (params) =>
|
||||
setElements((els) => {
|
||||
params.type = 'step';
|
||||
return addEdge(params, els);
|
||||
});
|
||||
|
||||
return (
|
||||
<TeaserFlow
|
||||
title="Additional Components"
|
||||
description="React Flow includes a MiniMap, Controls, Background and a FlowProvider you can use to access internal state outside the ReactFlow component."
|
||||
linesBg
|
||||
>
|
||||
<ReactFlowProvider>
|
||||
<ReactFlow
|
||||
elements={elements}
|
||||
onLoad={onLoad}
|
||||
zoomOnScroll={false}
|
||||
onConnect={onConnect}
|
||||
connectionLineType="step"
|
||||
>
|
||||
<Background variant="lines" gap={20} />
|
||||
<Controls showInteractive={false} />
|
||||
<MiniMap />
|
||||
</ReactFlow>
|
||||
</ReactFlowProvider>
|
||||
</TeaserFlow>
|
||||
);
|
||||
};
|
||||
@@ -1,141 +0,0 @@
|
||||
import React from 'react';
|
||||
import styled from '@emotion/styled';
|
||||
import { Flex, Box } from 'reflexbox';
|
||||
import Link from 'gatsby-link';
|
||||
import ReactFlow, {
|
||||
ReactFlowProvider,
|
||||
Background,
|
||||
Controls,
|
||||
MiniMap,
|
||||
} from 'react-flow-renderer';
|
||||
|
||||
import { H2, Text } from 'components/Typo';
|
||||
import Icon from 'components/Icon';
|
||||
import { baseColors } from 'themes';
|
||||
import { device, getThemeSpacePx } from 'utils/css-utils';
|
||||
|
||||
const Wrapper = styled(Flex)`
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
`;
|
||||
|
||||
const ReactFlowWrapper = styled(Box)`
|
||||
height: 400px;
|
||||
background: ${(p) => (p.isDark ? baseColors.violet : 'white')};
|
||||
border-radius: 10px;
|
||||
order: 2;
|
||||
margin: ${getThemeSpacePx(4)} ${getThemeSpacePx(0)};
|
||||
border: solid rgba(26, 25, 43, 0.054) 1.5px;
|
||||
|
||||
box-shadow: 0 2.8px 2.2px rgba(26, 25, 43, 0.014),
|
||||
0 12.5px 10px rgba(26, 25, 43, 0.02),
|
||||
0 22.3px 17.9px rgba(26, 25, 43, 0.022),
|
||||
0 41.8px 33.4px rgba(26, 25, 43, 0.026), 0 100px 80px rgba(26, 25, 43, 0.02);
|
||||
|
||||
@media ${device.tablet} {
|
||||
order: ${(p) => p.order};
|
||||
}
|
||||
|
||||
@media ${device.mobile} {
|
||||
margin-top: ${getThemeSpacePx(3)};
|
||||
}
|
||||
|
||||
.react-flow__controls {
|
||||
opacity: ${(p) => (p.isDark ? 0.5 : 1)};
|
||||
}
|
||||
`;
|
||||
|
||||
const DocsLink = styled(Link)`
|
||||
display: flex;
|
||||
font-weight: bold;
|
||||
align-items: center;
|
||||
margin-top: 16px;
|
||||
|
||||
@media ${device.mobile} {
|
||||
margin-top: ${getThemeSpacePx(0)};
|
||||
}
|
||||
|
||||
svg {
|
||||
transform: translateX(0px);
|
||||
transition: all 0.125s ease-in-out;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
svg {
|
||||
transform: translateX(5px);
|
||||
transition: all 0.125s ease-in-out;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const DescriptionWrapper = styled(Box)`
|
||||
order: 1;
|
||||
margin-bottom: ${getThemeSpacePx(3)};
|
||||
|
||||
@media ${device.tablet} {
|
||||
order: ${(p) => p.order};
|
||||
}
|
||||
|
||||
@media ${device.mobile} {
|
||||
margin-bottom: ${getThemeSpacePx(0)};
|
||||
}
|
||||
`;
|
||||
|
||||
const Description = ({ title, description }) => (
|
||||
<DescriptionWrapper width={[1, 1, 0.35]}>
|
||||
<H2>{title}</H2>
|
||||
<Text style={{ opacity: 0.7 }}>{description}</Text>
|
||||
<DocsLink to="/docs">
|
||||
Documentation{' '}
|
||||
<Icon width={42} name="arrow_right" colorizeStroke strokeColor="red" />
|
||||
</DocsLink>
|
||||
</DescriptionWrapper>
|
||||
);
|
||||
|
||||
export default ({
|
||||
title,
|
||||
description,
|
||||
textPosition = 'left',
|
||||
flowProps,
|
||||
withControls = false,
|
||||
withMinimap = false,
|
||||
isDark = false,
|
||||
linesBg = false,
|
||||
children,
|
||||
}) => {
|
||||
const bgColor = isDark ? baseColors.violetLighten60 : baseColors.violet;
|
||||
const reactFlowOrder = textPosition === 'left' ? 2 : 1;
|
||||
|
||||
return (
|
||||
<Wrapper mb={[6, 6, 7]}>
|
||||
{textPosition === 'left' && (
|
||||
<Description order={1} title={title} description={description} />
|
||||
)}
|
||||
<ReactFlowWrapper
|
||||
width={[1, 1, 0.6]}
|
||||
isDark={isDark}
|
||||
order={reactFlowOrder}
|
||||
>
|
||||
{children ? (
|
||||
children
|
||||
) : (
|
||||
<ReactFlowProvider>
|
||||
<ReactFlow {...flowProps} zoomOnScroll={false}>
|
||||
<Background
|
||||
color={linesBg ? '#eee' : bgColor}
|
||||
gap={15}
|
||||
variant={linesBg ? 'lines' : 'dots'}
|
||||
/>
|
||||
{withControls && <Controls />}
|
||||
{withMinimap && <MiniMap />}
|
||||
</ReactFlow>
|
||||
</ReactFlowProvider>
|
||||
)}
|
||||
</ReactFlowWrapper>
|
||||
{textPosition !== 'left' && (
|
||||
<Description order={2} title={title} description={description} />
|
||||
)}
|
||||
</Wrapper>
|
||||
);
|
||||
};
|
||||
@@ -1,101 +0,0 @@
|
||||
import styled from '@emotion/styled';
|
||||
import { Box } from 'reflexbox';
|
||||
|
||||
import { getThemeSpacePx, getThemeColor, device } from 'utils/css-utils';
|
||||
|
||||
export const H1 = styled.h1`
|
||||
font-size: ${(p) => p.theme.fontSizesPx[4]};
|
||||
line-height: 1.1;
|
||||
letter-spacing: 1px;
|
||||
font-weight: 900;
|
||||
margin: ${getThemeSpacePx(3)} 0;
|
||||
|
||||
@media ${device.tablet} {
|
||||
font-size: ${(p) => p.theme.fontSizesPx[5]};
|
||||
}
|
||||
`;
|
||||
|
||||
export const H2 = styled.h2`
|
||||
font-size: ${(p) => p.theme.fontSizesPx[3]};
|
||||
line-height: 1.1;
|
||||
letter-spacing: 1px;
|
||||
font-weight: 900;
|
||||
margin: ${getThemeSpacePx(3)} 0;
|
||||
|
||||
@media ${device.tablet} {
|
||||
font-size: ${(p) => p.theme.fontSizesPx[4]};
|
||||
}
|
||||
`;
|
||||
|
||||
export const H3 = styled.h3`
|
||||
font-size: ${(p) => p.theme.fontSizesPx[2]};
|
||||
line-height: 1.4;
|
||||
letter-spacing: 1px;
|
||||
font-weight: 900;
|
||||
margin: ${getThemeSpacePx(3)} 0;
|
||||
|
||||
@media ${device.tablet} {
|
||||
font-size: ${(p) => p.theme.fontSizesPx[3]};
|
||||
}
|
||||
`;
|
||||
|
||||
export const H4 = styled.h4`
|
||||
font-size: ${(p) => p.theme.fontSizesPx[1]};
|
||||
line-height: 1.3;
|
||||
letter-spacing: 1px;
|
||||
font-weight: 900;
|
||||
margin: ${getThemeSpacePx(2)} 0;
|
||||
|
||||
@media ${device.tablet} {
|
||||
font-size: ${(p) => p.theme.fontSizesPx[2]};
|
||||
}
|
||||
`;
|
||||
|
||||
export const H5 = H4;
|
||||
|
||||
export const UL = styled.ul`
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
`;
|
||||
|
||||
const BulletPointSvg = encodeURIComponent(
|
||||
`<svg width="10" height="10" viewBox="0 0 10 10" fill="none" xmlns="http://www.w3.org/2000/svg"><circle cx="5" cy="5" r="2" fill="#F7F8F8"/></svg>`
|
||||
);
|
||||
|
||||
export const LI = styled.li`
|
||||
color: ${getThemeColor('textLight')};
|
||||
list-style-image: url('data:image/svg+xml;utf8,${BulletPointSvg}');
|
||||
`;
|
||||
|
||||
export const Text = styled(Box)`
|
||||
font-size: ${(p) => p.theme.fontSizesPx[p.fontSize || 1]};
|
||||
line-height: 1.6;
|
||||
`;
|
||||
|
||||
export const TextLight = styled(Text)`
|
||||
color: ${getThemeColor('textLight')};
|
||||
`;
|
||||
|
||||
export const Label = styled(Box)`
|
||||
font-size: 12px;
|
||||
line-height: 1.5;
|
||||
letter-spacing: 1px;
|
||||
`;
|
||||
|
||||
export const AttributionText = styled(TextLight)`
|
||||
text-align: center;
|
||||
margin-top: 12px;
|
||||
font-size: ${(p) => p.theme.fontSizesPx[0]};
|
||||
`;
|
||||
|
||||
export const Paragraph = styled(Box)`
|
||||
max-width: 520px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
letter-spacing: 0.5px;
|
||||
line-height: 1.5;
|
||||
|
||||
code {
|
||||
color: ${(p) => p.theme.colors.violet};
|
||||
}
|
||||
`;
|
||||
@@ -1,25 +0,0 @@
|
||||
import React from 'react';
|
||||
|
||||
export default ({
|
||||
sourceX,
|
||||
sourceY,
|
||||
sourcePosition,
|
||||
targetX,
|
||||
targetY,
|
||||
targetPosition,
|
||||
connectionLineType,
|
||||
connectionLineStyle,
|
||||
}) => {
|
||||
return (
|
||||
<g>
|
||||
<path
|
||||
fill="none"
|
||||
stroke="#222"
|
||||
strokeWidth={1.5}
|
||||
className="animated"
|
||||
d={`M${sourceX},${sourceY} C ${sourceX} ${targetY} ${sourceX} ${targetY} ${targetX},${targetY}`}
|
||||
/>
|
||||
<circle cx={targetX} cy={targetY} fill="#fff" r={3} stroke="#222" strokeWidth={1.5} />
|
||||
</g>
|
||||
);
|
||||
};
|
||||
@@ -1,37 +0,0 @@
|
||||
import React, { useState } from 'react';
|
||||
import ReactFlow, {
|
||||
removeElements,
|
||||
addEdge,
|
||||
Background,
|
||||
} from 'react-flow-renderer';
|
||||
|
||||
import ConnectionLine from './ConnectionLine';
|
||||
|
||||
const initialElements = [
|
||||
{
|
||||
id: 'connectionline-1',
|
||||
type: 'input',
|
||||
data: { label: 'Node 1' },
|
||||
position: { x: 250, y: 5 },
|
||||
},
|
||||
];
|
||||
|
||||
const ConnectionLineFlow = () => {
|
||||
const [elements, setElements] = useState(initialElements);
|
||||
const onElementsRemove = (elementsToRemove) =>
|
||||
setElements((els) => removeElements(elementsToRemove, els));
|
||||
const onConnect = (params) => setElements((els) => addEdge(params, els));
|
||||
|
||||
return (
|
||||
<ReactFlow
|
||||
elements={elements}
|
||||
connectionLineComponent={ConnectionLine}
|
||||
onElementsRemove={onElementsRemove}
|
||||
onConnect={onConnect}
|
||||
>
|
||||
<Background variant="lines" />
|
||||
</ReactFlow>
|
||||
);
|
||||
};
|
||||
|
||||
export default ConnectionLineFlow;
|
||||
@@ -1,37 +0,0 @@
|
||||
import React, { memo } from 'react';
|
||||
|
||||
import { Handle } from 'react-flow-renderer';
|
||||
|
||||
export default memo(({ data }) => {
|
||||
return (
|
||||
<>
|
||||
<Handle
|
||||
type="target"
|
||||
position="left"
|
||||
style={{ background: '#555' }}
|
||||
onConnect={(params) => console.log('handle onConnect', params)}
|
||||
/>
|
||||
<div>
|
||||
Custom Color Picker Node: <strong>{data.color}</strong>
|
||||
</div>
|
||||
<input
|
||||
className="nodrag"
|
||||
type="color"
|
||||
onChange={data.onChange}
|
||||
defaultValue={data.color}
|
||||
/>
|
||||
<Handle
|
||||
type="source"
|
||||
position="right"
|
||||
id="a"
|
||||
style={{ top: 10, background: '#555' }}
|
||||
/>
|
||||
<Handle
|
||||
type="source"
|
||||
position="right"
|
||||
id="b"
|
||||
style={{ bottom: 10, top: 'auto', background: '#555' }}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
});
|
||||
@@ -1,7 +0,0 @@
|
||||
.react-flow__node-selectorNode {
|
||||
font-size: 12px;
|
||||
background: #eee;
|
||||
border: 1px solid #555;
|
||||
border-radius: 5px;
|
||||
text-align: center;
|
||||
}
|
||||
@@ -1,149 +0,0 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
|
||||
import ReactFlow, {
|
||||
isEdge,
|
||||
removeElements,
|
||||
addEdge,
|
||||
MiniMap,
|
||||
Controls,
|
||||
} from 'react-flow-renderer';
|
||||
|
||||
import ColorSelectorNode from './ColorSelectorNode';
|
||||
|
||||
import './index.css';
|
||||
|
||||
const onLoad = (reactFlowInstance) => {
|
||||
console.log('flow loaded:', reactFlowInstance);
|
||||
setTimeout(() => reactFlowInstance.fitView(), 1);
|
||||
};
|
||||
const onNodeDragStop = (event, node) => console.log('drag stop', node);
|
||||
const onElementClick = (event, element) => console.log('click', element);
|
||||
|
||||
const initBgColor = '#1A192B';
|
||||
|
||||
const connectionLineStyle = { stroke: '#fff' };
|
||||
const snapGrid = [20, 20];
|
||||
const nodeTypes = {
|
||||
selectorNode: ColorSelectorNode,
|
||||
};
|
||||
|
||||
const CustomNodeFlow = () => {
|
||||
const [elements, setElements] = useState([]);
|
||||
const [bgColor, setBgColor] = useState(initBgColor);
|
||||
|
||||
useEffect(() => {
|
||||
const onChange = (event) => {
|
||||
setElements((els) =>
|
||||
els.map((e) => {
|
||||
if (isEdge(e) || e.id !== '2') {
|
||||
return e;
|
||||
}
|
||||
|
||||
const color = event.target.value;
|
||||
|
||||
setBgColor(color);
|
||||
|
||||
return {
|
||||
...e,
|
||||
data: {
|
||||
...e.data,
|
||||
color,
|
||||
},
|
||||
};
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
setElements([
|
||||
{
|
||||
id: '1',
|
||||
type: 'input',
|
||||
data: { label: 'An input node' },
|
||||
position: { x: 0, y: 50 },
|
||||
sourcePosition: 'right',
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
type: 'selectorNode',
|
||||
data: { onChange: onChange, color: initBgColor },
|
||||
style: { border: '1px solid #777', padding: 10 },
|
||||
position: { x: 300, y: 50 },
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
type: 'output',
|
||||
data: { label: 'Output A' },
|
||||
position: { x: 650, y: 25 },
|
||||
targetPosition: 'left',
|
||||
},
|
||||
{
|
||||
id: '4',
|
||||
type: 'output',
|
||||
data: { label: 'Output B' },
|
||||
position: { x: 650, y: 100 },
|
||||
targetPosition: 'left',
|
||||
},
|
||||
|
||||
{
|
||||
id: 'e1-2',
|
||||
source: '1',
|
||||
target: '2',
|
||||
animated: true,
|
||||
style: { stroke: '#fff' },
|
||||
},
|
||||
{
|
||||
id: 'e2a-3',
|
||||
source: '2__a',
|
||||
target: '3',
|
||||
animated: true,
|
||||
style: { stroke: '#fff' },
|
||||
},
|
||||
{
|
||||
id: 'e2b-4',
|
||||
source: '2__b',
|
||||
target: '4',
|
||||
animated: true,
|
||||
style: { stroke: '#fff' },
|
||||
},
|
||||
]);
|
||||
}, []);
|
||||
|
||||
const onElementsRemove = (elementsToRemove) =>
|
||||
setElements((els) => removeElements(elementsToRemove, els));
|
||||
const onConnect = (params) =>
|
||||
setElements((els) =>
|
||||
addEdge({ ...params, animated: true, style: { stroke: '#fff' } }, els)
|
||||
);
|
||||
|
||||
return (
|
||||
<ReactFlow
|
||||
elements={elements}
|
||||
onElementClick={onElementClick}
|
||||
onElementsRemove={onElementsRemove}
|
||||
onConnect={onConnect}
|
||||
onNodeDragStop={onNodeDragStop}
|
||||
style={{ background: bgColor }}
|
||||
onLoad={onLoad}
|
||||
nodeTypes={nodeTypes}
|
||||
connectionLineStyle={connectionLineStyle}
|
||||
snapToGrid={true}
|
||||
snapGrid={snapGrid}
|
||||
defaultZoom={1.5}
|
||||
>
|
||||
<MiniMap
|
||||
nodeStrokeColor={(n) => {
|
||||
if (n.type === 'input') return '#0041d0';
|
||||
if (n.type === 'selectorNode') return bgColor;
|
||||
if (n.type === 'output') return '#ff0072';
|
||||
}}
|
||||
nodeColor={(n) => {
|
||||
if (n.type === 'selectorNode') return bgColor;
|
||||
return '#fff';
|
||||
}}
|
||||
/>
|
||||
<Controls />
|
||||
</ReactFlow>
|
||||
);
|
||||
};
|
||||
|
||||
export default CustomNodeFlow;
|
||||
@@ -1,36 +0,0 @@
|
||||
/**
|
||||
* Example for checking the different edge types and source and target positions
|
||||
*/
|
||||
import React, { useState } from 'react';
|
||||
|
||||
import ReactFlow, { removeElements, addEdge, MiniMap, Controls, Background } from 'react-flow-renderer';
|
||||
import { getElements } from './utils';
|
||||
|
||||
const onLoad = (reactFlowInstance) => {
|
||||
reactFlowInstance.fitView();
|
||||
console.log(reactFlowInstance.getElements());
|
||||
};
|
||||
|
||||
const initialElements = getElements();
|
||||
|
||||
const EdgeTypesFlow = () => {
|
||||
const [elements, setElements] = useState(initialElements);
|
||||
const onElementsRemove = (elementsToRemove) => setElements((els) => removeElements(elementsToRemove, els));
|
||||
const onConnect = (params) => setElements((els) => addEdge(params, els));
|
||||
|
||||
return (
|
||||
<ReactFlow
|
||||
elements={elements}
|
||||
onLoad={onLoad}
|
||||
onElementsRemove={onElementsRemove}
|
||||
onConnect={onConnect}
|
||||
minZoom={0.2}
|
||||
>
|
||||
<MiniMap />
|
||||
<Controls />
|
||||
<Background />
|
||||
</ReactFlow>
|
||||
);
|
||||
};
|
||||
|
||||
export default EdgeTypesFlow;
|
||||
@@ -1,120 +0,0 @@
|
||||
const nodeWidth = 80;
|
||||
const nodeGapWidth = nodeWidth * 2;
|
||||
const nodeStyle = { width: nodeWidth, fontSize: 11, color: 'white' };
|
||||
|
||||
const sourceTargetPositions = [
|
||||
{ source: 'bottom', target: 'top' },
|
||||
{ source: 'right', target: 'left' },
|
||||
];
|
||||
const nodeColors = [
|
||||
['#1e9e99', '#4cb3ac', '#6ec9c0', '#8ddfd4'],
|
||||
['#0f4c75', '#1b5d8b', '#276fa1', '#3282b8'],
|
||||
];
|
||||
const edgeTypes = ['default', 'step', 'smoothstep', 'straight'];
|
||||
const offsets = [
|
||||
{
|
||||
x: 0,
|
||||
y: -nodeGapWidth,
|
||||
},
|
||||
{
|
||||
x: nodeGapWidth,
|
||||
y: -nodeGapWidth,
|
||||
},
|
||||
{
|
||||
x: nodeGapWidth,
|
||||
y: 0,
|
||||
},
|
||||
{
|
||||
x: nodeGapWidth,
|
||||
y: nodeGapWidth,
|
||||
},
|
||||
{
|
||||
x: 0,
|
||||
y: nodeGapWidth,
|
||||
},
|
||||
{
|
||||
x: -nodeGapWidth,
|
||||
y: nodeGapWidth,
|
||||
},
|
||||
{
|
||||
x: -nodeGapWidth,
|
||||
y: 0,
|
||||
},
|
||||
{
|
||||
x: -nodeGapWidth,
|
||||
y: -nodeGapWidth,
|
||||
},
|
||||
];
|
||||
|
||||
let id = 0;
|
||||
const getNodeId = () => `edgetypes-${(id++).toString()}`;
|
||||
|
||||
export function getElements() {
|
||||
const initialElements = [];
|
||||
|
||||
for (
|
||||
let sourceTargetIndex = 0;
|
||||
sourceTargetIndex < sourceTargetPositions.length;
|
||||
sourceTargetIndex++
|
||||
) {
|
||||
const currSourceTargetPos = sourceTargetPositions[sourceTargetIndex];
|
||||
|
||||
for (
|
||||
let edgeTypeIndex = 0;
|
||||
edgeTypeIndex < edgeTypes.length;
|
||||
edgeTypeIndex++
|
||||
) {
|
||||
const currEdgeType = edgeTypes[edgeTypeIndex];
|
||||
|
||||
for (let offsetIndex = 0; offsetIndex < offsets.length; offsetIndex++) {
|
||||
const currOffset = offsets[offsetIndex];
|
||||
|
||||
const style = {
|
||||
...nodeStyle,
|
||||
background: nodeColors[sourceTargetIndex][edgeTypeIndex],
|
||||
};
|
||||
const sourcePosition = {
|
||||
x: offsetIndex * nodeWidth * 4,
|
||||
y: edgeTypeIndex * 300 + sourceTargetIndex * edgeTypes.length * 300,
|
||||
};
|
||||
const sourceId = getNodeId();
|
||||
const sourceData = { label: `Source ${sourceId}` };
|
||||
const sourceNode = {
|
||||
id: sourceId,
|
||||
style,
|
||||
data: sourceData,
|
||||
position: sourcePosition,
|
||||
sourcePosition: currSourceTargetPos.source,
|
||||
targetPosition: currSourceTargetPos.target,
|
||||
};
|
||||
|
||||
const targetId = getNodeId();
|
||||
const targetData = { label: `Target ${targetId}` };
|
||||
const targetPosition = {
|
||||
x: sourcePosition.x + currOffset.x,
|
||||
y: sourcePosition.y + currOffset.y,
|
||||
};
|
||||
const targetNode = {
|
||||
id: targetId,
|
||||
style,
|
||||
data: targetData,
|
||||
position: targetPosition,
|
||||
sourcePosition: currSourceTargetPos.source,
|
||||
targetPosition: currSourceTargetPos.target,
|
||||
};
|
||||
|
||||
initialElements.push(sourceNode);
|
||||
initialElements.push(targetNode);
|
||||
|
||||
initialElements.push({
|
||||
id: `${sourceId}-${targetId}`,
|
||||
source: sourceId,
|
||||
target: targetId,
|
||||
type: currEdgeType,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return initialElements;
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
import React from 'react';
|
||||
import { getBezierPath, getMarkerEnd } from 'react-flow-renderer';
|
||||
|
||||
export default function CustomEdge({
|
||||
id,
|
||||
sourceX,
|
||||
sourceY,
|
||||
targetX,
|
||||
targetY,
|
||||
sourcePosition,
|
||||
targetPosition,
|
||||
style = {},
|
||||
data,
|
||||
arrowHeadType,
|
||||
markerEndId,
|
||||
}) {
|
||||
const edgePath = getBezierPath({ sourceX, sourceY, sourcePosition, targetX, targetY, targetPosition });
|
||||
const markerEnd = getMarkerEnd(arrowHeadType, markerEndId);
|
||||
|
||||
return (
|
||||
<>
|
||||
<path id={id} style={style} className="react-flow__edge-path" d={edgePath} markerEnd={markerEnd} />
|
||||
<text>
|
||||
<textPath href={`#${id}`} style={{ fontSize: '12px' }} startOffset="50%" textAnchor="middle">
|
||||
{data.text}
|
||||
</textPath>
|
||||
</text>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -1,150 +0,0 @@
|
||||
import React, { useState } from 'react';
|
||||
|
||||
import ReactFlow, {
|
||||
removeElements,
|
||||
addEdge,
|
||||
MiniMap,
|
||||
Controls,
|
||||
Background,
|
||||
} from 'react-flow-renderer';
|
||||
|
||||
import CustomEdge from './CustomEdge';
|
||||
|
||||
const onLoad = (reactFlowInstance) => reactFlowInstance.fitView();
|
||||
const onNodeDragStop = (event, node) => console.log('drag stop', node);
|
||||
const onElementClick = (event, element) => console.log('click', element);
|
||||
|
||||
const initialElements = [
|
||||
{
|
||||
id: 'edges-1',
|
||||
type: 'input',
|
||||
data: { label: 'Input 1' },
|
||||
position: { x: 250, y: 0 },
|
||||
},
|
||||
{ id: 'edges-2', data: { label: 'Node 2' }, position: { x: 150, y: 100 } },
|
||||
{ id: 'edges-2a', data: { label: 'Node 2a' }, position: { x: 0, y: 180 } },
|
||||
{ id: 'edges-3', data: { label: 'Node 3' }, position: { x: 250, y: 200 } },
|
||||
{ id: 'edges-4', data: { label: 'Node 4' }, position: { x: 400, y: 300 } },
|
||||
{ id: 'edges-3a', data: { label: 'Node 3a' }, position: { x: 150, y: 300 } },
|
||||
{ id: 'edges-5', data: { label: 'Node 5' }, position: { x: 250, y: 400 } },
|
||||
{
|
||||
id: 'edges-6',
|
||||
type: 'output',
|
||||
data: { label: 'Output 6' },
|
||||
position: { x: 50, y: 550 },
|
||||
},
|
||||
{
|
||||
id: 'edges-7',
|
||||
type: 'output',
|
||||
data: { label: 'Output 7' },
|
||||
position: { x: 250, y: 550 },
|
||||
},
|
||||
{
|
||||
id: 'edges-8',
|
||||
type: 'output',
|
||||
data: { label: 'Output 8' },
|
||||
position: { x: 525, y: 600 },
|
||||
},
|
||||
{
|
||||
id: 'edges-e1-2',
|
||||
source: 'edges-1',
|
||||
target: 'edges-2',
|
||||
label: 'bezier edge (default)',
|
||||
className: 'normal-edge',
|
||||
},
|
||||
{
|
||||
id: 'edges-e2-2a',
|
||||
source: 'edges-2',
|
||||
target: 'edges-2a',
|
||||
type: 'smoothstep',
|
||||
label: 'smoothstep edge',
|
||||
},
|
||||
{
|
||||
id: 'edges-e2-3',
|
||||
source: 'edges-2',
|
||||
target: 'edges-3',
|
||||
type: 'step',
|
||||
label: 'step edge',
|
||||
},
|
||||
{
|
||||
id: 'edges-e3-4',
|
||||
source: 'edges-3',
|
||||
target: 'edges-4',
|
||||
type: 'straight',
|
||||
label: 'straight edge',
|
||||
},
|
||||
{
|
||||
id: 'edges-e3-3a',
|
||||
source: 'edges-3',
|
||||
target: 'edges-3a',
|
||||
type: 'straight',
|
||||
label: 'label only edge',
|
||||
style: { stroke: 'none' },
|
||||
},
|
||||
{
|
||||
id: 'edges-e3-5',
|
||||
source: 'edges-4',
|
||||
target: 'edges-5',
|
||||
animated: true,
|
||||
label: 'animated styled edge',
|
||||
style: { stroke: 'red' },
|
||||
},
|
||||
{
|
||||
id: 'edges-e5-6',
|
||||
source: 'edges-5',
|
||||
target: 'edges-6',
|
||||
label: 'styled label',
|
||||
labelStyle: { fill: 'red', fontWeight: 700 },
|
||||
arrowHeadType: 'arrow',
|
||||
},
|
||||
{
|
||||
id: 'edges-e5-7',
|
||||
source: 'edges-5',
|
||||
target: 'edges-7',
|
||||
label: 'label with styled bg',
|
||||
labelBgPadding: [8, 4],
|
||||
labelBgBorderRadius: 4,
|
||||
labelBgStyle: { fill: '#FFCC00', color: '#fff', fillOpacity: 0.7 },
|
||||
arrowHeadType: 'arrowclosed',
|
||||
},
|
||||
{
|
||||
id: 'edges-e5-8',
|
||||
source: 'edges-5',
|
||||
target: 'edges-8',
|
||||
type: 'custom',
|
||||
data: { text: 'custom edge' },
|
||||
arrowHeadType: 'arrowclosed',
|
||||
},
|
||||
];
|
||||
|
||||
const edgeTypes = {
|
||||
custom: CustomEdge,
|
||||
};
|
||||
|
||||
const EdgesFlow = () => {
|
||||
const [elements, setElements] = useState(initialElements);
|
||||
|
||||
const onElementsRemove = (elementsToRemove) =>
|
||||
setElements((els) => removeElements(elementsToRemove, els));
|
||||
const onConnect = (params) => setElements((els) => addEdge(params, els));
|
||||
|
||||
return (
|
||||
<ReactFlow
|
||||
elements={elements}
|
||||
onElementClick={onElementClick}
|
||||
onElementsRemove={onElementsRemove}
|
||||
onConnect={onConnect}
|
||||
onNodeDragStop={onNodeDragStop}
|
||||
onLoad={onLoad}
|
||||
snapToGrid={true}
|
||||
edgeTypes={edgeTypes}
|
||||
key="edges"
|
||||
>
|
||||
<MiniMap />
|
||||
<Controls />
|
||||
<Background />
|
||||
</ReactFlow>
|
||||
);
|
||||
};
|
||||
|
||||
export default EdgesFlow;
|
||||
@@ -1,58 +0,0 @@
|
||||
import React, { useState } from 'react';
|
||||
|
||||
import ReactFlow, {
|
||||
removeElements,
|
||||
addEdge,
|
||||
MiniMap,
|
||||
Controls,
|
||||
Background,
|
||||
} from 'react-flow-renderer';
|
||||
|
||||
const onLoad = (reactFlowInstance) =>
|
||||
console.log('flow loaded:', reactFlowInstance);
|
||||
const onElementClick = (event, element) => console.log('click', element);
|
||||
const onNodeDragStop = (event, node) => console.log('drag stop', node);
|
||||
|
||||
const EmptyFlow = () => {
|
||||
const [elements, setElements] = useState([]);
|
||||
const onElementsRemove = (elementsToRemove) =>
|
||||
setElements((els) => removeElements(elementsToRemove, els));
|
||||
const onConnect = (params) => setElements((els) => addEdge(params, els));
|
||||
const addRandomNode = () => {
|
||||
const nodeId = `nodes-${(elements.length + 1).toString()}`;
|
||||
const newNode = {
|
||||
id: nodeId,
|
||||
data: { label: `Node: ${nodeId}` },
|
||||
position: {
|
||||
x: Math.random() * window.innerWidth,
|
||||
y: Math.random() * window.innerHeight,
|
||||
},
|
||||
};
|
||||
setElements((els) => els.concat(newNode));
|
||||
};
|
||||
|
||||
return (
|
||||
<ReactFlow
|
||||
elements={elements}
|
||||
onLoad={onLoad}
|
||||
onElementClick={onElementClick}
|
||||
onElementsRemove={onElementsRemove}
|
||||
onConnect={(p) => onConnect(p)}
|
||||
onNodeDragStop={onNodeDragStop}
|
||||
>
|
||||
<MiniMap />
|
||||
<Controls />
|
||||
<Background variant="lines" />
|
||||
|
||||
<button
|
||||
type="button"
|
||||
onClick={addRandomNode}
|
||||
style={{ position: 'absolute', left: 10, top: 10, zIndex: 4 }}
|
||||
>
|
||||
add node
|
||||
</button>
|
||||
</ReactFlow>
|
||||
);
|
||||
};
|
||||
|
||||
export default EmptyFlow;
|
||||
@@ -1,58 +0,0 @@
|
||||
import React, { useState } from 'react';
|
||||
|
||||
import ReactFlow, { addEdge, MiniMap, Controls } from 'react-flow-renderer';
|
||||
import { useEffect } from 'react';
|
||||
|
||||
const initialElements = [
|
||||
{
|
||||
id: 'hidden-1',
|
||||
type: 'input',
|
||||
data: { label: 'Node 1' },
|
||||
position: { x: 250, y: 5 },
|
||||
},
|
||||
{ id: 'hidden-2', data: { label: 'Node 2' }, position: { x: 100, y: 100 } },
|
||||
{ id: 'hidden-3', data: { label: 'Node 3' }, position: { x: 400, y: 100 } },
|
||||
{ id: 'hidden-4', data: { label: 'Node 4' }, position: { x: 400, y: 200 } },
|
||||
{ id: 'hidden-e1-2', source: 'hidden-1', target: 'hidden-2' },
|
||||
{ id: 'hidden-e1-3', source: 'hidden-1', target: 'hidden-3' },
|
||||
{ id: 'hidden-e3-4', source: 'hidden-3', target: 'hidden-4' },
|
||||
];
|
||||
|
||||
const HiddenFlow = () => {
|
||||
const [elements, setElements] = useState(initialElements);
|
||||
const [isHidden, setIsHidden] = useState(false);
|
||||
const onConnect = (params) => setElements((els) => addEdge(params, els));
|
||||
|
||||
useEffect(() => {
|
||||
setElements((els) =>
|
||||
els.map((e) => {
|
||||
e.isHidden = isHidden;
|
||||
return e;
|
||||
})
|
||||
);
|
||||
}, [isHidden]);
|
||||
|
||||
return (
|
||||
<ReactFlow elements={elements} onConnect={onConnect}>
|
||||
<MiniMap />
|
||||
<Controls />
|
||||
|
||||
<div style={{ position: 'absolute', left: 10, top: 10, zIndex: 4 }}>
|
||||
<div>
|
||||
<label htmlFor="ishidden">
|
||||
isHidden
|
||||
<input
|
||||
id="ishidden"
|
||||
type="checkbox"
|
||||
checked={isHidden}
|
||||
onChange={(event) => setIsHidden(event.target.checked)}
|
||||
className="react-flow__ishidden"
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</ReactFlow>
|
||||
);
|
||||
};
|
||||
|
||||
export default HiddenFlow;
|
||||