├── .eslintrc.js
├── .gitignore
├── .npmrc
├── .nvmrc
├── .prettierignore
├── .prettierrc
├── LICENSE
├── README.md
├── gatsby-browser.js
├── gatsby-config.js
├── gatsby-node.js
├── generators
├── docs.js
├── examples.js
├── markdown-pages.js
└── utils.js
├── netlify.toml
├── package-lock.json
├── package.json
├── src
├── assets
│ ├── data
│ │ ├── examples.json
│ │ └── showcases.json
│ ├── icons
│ │ ├── arrow_right.svg
│ │ ├── arrow_right_circle.svg
│ │ ├── close.svg
│ │ ├── code.svg
│ │ ├── eye.svg
│ │ ├── favicon_stroke.svg
│ │ ├── github_circle.svg
│ │ ├── graph_bar.svg
│ │ ├── info_circle.svg
│ │ ├── lightning.svg
│ │ ├── link.svg
│ │ ├── mail.svg
│ │ ├── menu.svg
│ │ ├── pen.svg
│ │ ├── phone.svg
│ │ ├── twitter_circle.svg
│ │ └── twitter_outline.svg
│ └── images
│ │ ├── blog-webkid-teaser.jpg
│ │ ├── logo-white.svg
│ │ ├── logo.svg
│ │ ├── react-flow-logo.svg
│ │ └── showcases
│ │ ├── botfront-showcase.png
│ │ ├── circles-showcase.png
│ │ └── datablocks-showcase.png
├── components
│ ├── Button
│ │ └── index.js
│ ├── CenterContent
│ │ └── index.js
│ ├── Close
│ │ └── index.js
│ ├── CodeBlock
│ │ ├── Mdx.js
│ │ └── index.js
│ ├── ContentSection
│ │ └── index.js
│ ├── Footer
│ │ └── index.js
│ ├── Header
│ │ └── index.js
│ ├── HeroFlow
│ │ ├── ColorPickerNode.js
│ │ └── index.js
│ ├── Icon
│ │ └── index.js
│ ├── InfoBox
│ │ └── index.js
│ ├── Link
│ │ └── index.js
│ ├── Logo
│ │ └── index.js
│ ├── Page
│ │ ├── Doc.js
│ │ ├── Example.js
│ │ ├── MetaTags.js
│ │ └── index.js
│ ├── SectionIntro
│ │ └── index.js
│ ├── Showcases
│ │ └── index.js
│ ├── Sidebar
│ │ └── index.js
│ ├── TeaserFlow
│ │ ├── A.js
│ │ ├── B.js
│ │ ├── C.js
│ │ └── index.js
│ └── Typo
│ │ └── index.js
├── example-flows
│ ├── ContextualZoomFeatures
│ │ ├── ZoomNode.js
│ │ ├── index.css
│ │ └── index.js
│ ├── CustomConnectionLine
│ │ ├── ConnectionLine.js
│ │ └── index.js
│ ├── CustomNode
│ │ ├── ColorSelectorNode.js
│ │ ├── index.css
│ │ └── index.js
│ ├── DragHandle
│ │ ├── DragHandleNode.tsx
│ │ └── index.tsx
│ ├── DragNDrop
│ │ ├── Sidebar.js
│ │ ├── dnd.css
│ │ └── index.js
│ ├── EdgeTypes
│ │ ├── index.js
│ │ └── utils.js
│ ├── EdgeWithButton
│ │ ├── ButtonEdge.js
│ │ ├── index.css
│ │ └── index.js
│ ├── Edges
│ │ ├── CustomEdge.js
│ │ └── index.js
│ ├── Empty
│ │ └── index.js
│ ├── FloatingEdges
│ │ ├── FloatingConnectionLine.tsx
│ │ ├── FloatingEdge.tsx
│ │ ├── index.tsx
│ │ ├── style.css
│ │ └── utils.ts
│ ├── Hidden
│ │ └── index.js
│ ├── Horizontal
│ │ └── index.js
│ ├── Interaction
│ │ └── index.js
│ ├── Layouting
│ │ ├── index.js
│ │ ├── initial-elements.js
│ │ └── layouting.css
│ ├── Overview
│ │ ├── index.js
│ │ └── initial-elements.js
│ ├── Provider
│ │ ├── Sidebar.js
│ │ ├── index.js
│ │ └── provider.css
│ ├── SaveRestore
│ │ ├── index.js
│ │ └── save.css
│ ├── SmoothTransition
│ │ ├── index.js
│ │ └── transition.css
│ ├── Stress
│ │ ├── index.js
│ │ └── utils.js
│ ├── UpdatableEdge
│ │ └── index.js
│ ├── UpdateNode
│ │ ├── index.js
│ │ └── updatenode.css
│ ├── Validation
│ │ ├── index.js
│ │ └── validation.css
│ └── ZoomPanHelper
│ │ ├── Sidebar.js
│ │ ├── index.js
│ │ └── zoompanhelper.css
├── hooks
│ ├── useExamplePages.js
│ ├── useMenuHeight.js
│ └── useShowcaseImages.js
├── markdown
│ └── docs
│ │ ├── api
│ │ ├── component-props.md
│ │ ├── components
│ │ │ ├── background.md
│ │ │ ├── controls.md
│ │ │ ├── minimap.md
│ │ │ └── provider.md
│ │ ├── edge-types.md
│ │ ├── edge-utils.md
│ │ ├── edges.md
│ │ ├── handle.md
│ │ ├── helper-functions.md
│ │ ├── hooks.md
│ │ ├── internal-state-actions.md
│ │ ├── node-types
│ │ │ ├── index.md
│ │ │ └── node-types.js
│ │ ├── nodes.md
│ │ └── react-flow-instance.md
│ │ ├── getting-started
│ │ ├── Basic.js
│ │ ├── BasicFunctions.js
│ │ └── index.md
│ │ ├── index.md
│ │ └── theming.md
├── pages
│ ├── 404.js
│ └── index.js
├── styles
│ └── global.css
├── templates
│ ├── doc-page.js
│ ├── example-page.js
│ └── mdx-renderer
│ │ ├── DefaultMdx.js
│ │ └── DocMdx.js
├── themes
│ ├── global.js
│ ├── index.js
│ └── normalize.js
└── utils
│ ├── browser-utils.js
│ ├── css-utils.js
│ └── project-utils.js
└── static
├── favicon.ico
├── fonts
├── JetBrainsMono-Bold.eot
├── JetBrainsMono-Bold.woff
├── JetBrainsMono-Bold.woff2
├── JetBrainsMono-Regular.eot
├── JetBrainsMono-Regular.woff
└── JetBrainsMono-Regular.woff2
├── images
└── react-flow-header.jpg
└── robots.txt
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | env: {
3 | browser: true,
4 | es6: true,
5 | },
6 | plugins: ['react'],
7 | globals: {
8 | graphql: false,
9 | __REACT_FLOW_VERSION__: true,
10 | },
11 | parserOptions: {
12 | sourceType: 'module',
13 | ecmaFeatures: {
14 | experimentalObjectRestSpread: true,
15 | jsx: true,
16 | },
17 | },
18 | extends: 'react-app',
19 | };
20 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | public
3 | .gatsby-context.js
4 | .DS_Store
5 | .intermediate-representation/
6 | .cache/
7 | yarn.lock
8 | *.log
9 | .env
10 | .yalc
11 | yalc.lock
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
1 | legacy-peer-deps=true
--------------------------------------------------------------------------------
/.nvmrc:
--------------------------------------------------------------------------------
1 | v15.10.0
2 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | src/pages/blog/**/*.md
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "trailingComma": "es5",
3 | "semi": true,
4 | "singleQuote": true,
5 | "printWidth": 120
6 | }
7 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License
2 |
3 | Copyright (c) webkid GmbH 2019-2022
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # react-flow-docs-v9
2 |
3 | #### ⚠️ These are the old docs for React Flow v9. For the up-to-date documentation, please refer to: [wbkd/react-flow-docs](https://github.com/wbkd/react-flow-docs) ⚠️
4 |
5 | ## Development
6 |
7 | - `npm install`
8 | - `npm run dev`
9 |
10 | ## Build
11 |
12 | - `npm run build`
13 |
--------------------------------------------------------------------------------
/gatsby-browser.js:
--------------------------------------------------------------------------------
1 | import 'babel-polyfill';
2 | import './src/styles/global.css';
3 |
4 | export const onRouteUpdate = () => {
5 | document.body.classList.remove('noscroll');
6 | };
7 |
--------------------------------------------------------------------------------
/gatsby-config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | siteMetadata: {
3 | title: `React Flow - Open Source Library`,
4 | siteUrl: `https://reactflow.dev`,
5 | description:
6 | 'React Flow is a highly customizable library for building interactive node-based editors, flow charts and diagrams.',
7 | },
8 | plugins: [
9 | 'gatsby-plugin-image',
10 | {
11 | resolve: `gatsby-source-filesystem`,
12 | options: {
13 | name: 'pages',
14 | path: `${__dirname}/src/pages`,
15 | },
16 | },
17 | {
18 | resolve: `gatsby-source-filesystem`,
19 | options: {
20 | name: 'exampleflows',
21 | path: `${__dirname}/src/example-flows`,
22 | },
23 | },
24 | {
25 | resolve: `gatsby-source-filesystem`,
26 | options: {
27 | name: `markdown`,
28 | path: `${__dirname}/src/markdown`,
29 | },
30 | },
31 | {
32 | resolve: `gatsby-source-filesystem`,
33 | options: {
34 | name: `assets`,
35 | path: `${__dirname}/src/assets`,
36 | ignore: [`**/.*`], // ignore files starting with a dot
37 | },
38 | },
39 | {
40 | resolve: `gatsby-plugin-manifest`,
41 | options: {
42 | icon: `src/assets/images/react-flow-logo.svg`,
43 | name: `React Flow`,
44 | short_name: `react-flow`,
45 | start_url: `/`,
46 | background_color: `#f7f0eb`,
47 | theme_color: `#1A192B`,
48 | display: `standalone`,
49 | lang: 'en',
50 | },
51 | },
52 | `gatsby-plugin-sharp`,
53 | `gatsby-remark-images`,
54 | {
55 | resolve: `gatsby-transformer-remark`,
56 | options: {
57 | plugins: [
58 | {
59 | resolve: `gatsby-remark-images`,
60 | options: {
61 | linkImagesToOriginal: false,
62 | },
63 | },
64 | ],
65 | },
66 | },
67 | {
68 | resolve: `gatsby-plugin-mdx`,
69 | options: {
70 | gatsbyRemarkPlugins: [
71 | {
72 | resolve: `gatsby-remark-images`,
73 | options: {
74 | maxWidth: 1200,
75 | showCaptions: true,
76 | quality: 85,
77 | backgroundColor: 'white',
78 | linkImagesToOriginal: false,
79 | },
80 | },
81 | 'gatsby-remark-copy-linked-files',
82 | ],
83 | remarkPlugins: [require('remark-unwrap-images')],
84 | extensions: [`.md`, `.mdx`],
85 | },
86 | },
87 | `gatsby-transformer-json`,
88 | `gatsby-plugin-react-helmet`,
89 | `gatsby-plugin-emotion`,
90 | {
91 | resolve: `gatsby-plugin-react-svg`,
92 | options: {
93 | rule: {
94 | include: /icons/,
95 | },
96 | },
97 | },
98 | `gatsby-transformer-sharp`,
99 | `gatsby-plugin-netlify`,
100 | `gatsby-transformer-javascript-frontmatter`,
101 | ],
102 | };
103 |
--------------------------------------------------------------------------------
/gatsby-node.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const { createFilePath } = require('gatsby-source-filesystem');
3 |
4 | const docsGenerator = require('./generators/docs');
5 | const exampleGenerator = require('./generators/examples');
6 |
7 | const pkg = require('./package.json');
8 |
9 | exports.createPages = ({ graphql, actions }) => {
10 | const { createPage } = actions;
11 |
12 | const generateDocs = docsGenerator(createPage, graphql);
13 | const generateExamples = exampleGenerator(createPage, graphql);
14 |
15 | return Promise.all([generateDocs, generateExamples]);
16 | };
17 |
18 | exports.onCreateNode = ({ node, actions, getNode }) => {
19 | const { createNodeField } = actions;
20 |
21 | if (node.internal.type === 'Mdx') {
22 | const value = createFilePath({ node, getNode });
23 |
24 | createNodeField({
25 | name: 'slug',
26 | node,
27 | value,
28 | });
29 | }
30 | };
31 |
32 | exports.onCreateWebpackConfig = ({ actions, plugins }) => {
33 | actions.setWebpackConfig({
34 | resolve: {
35 | modules: [path.resolve(__dirname, 'src'), 'node_modules'],
36 | alias: {
37 | 'example-flows': path.resolve(__dirname, 'src', 'example-flows'),
38 | },
39 | },
40 | devServer: {
41 | host: '0.0.0.0',
42 | },
43 | plugins: [
44 | plugins.define({
45 | __REACT_FLOW_VERSION__: JSON.stringify(
46 | pkg.dependencies['react-flow-renderer'].replace('^', '')
47 | ),
48 | }),
49 | ],
50 | });
51 | };
52 |
--------------------------------------------------------------------------------
/generators/docs.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 |
3 | const docsTemplate = path.resolve('./src/templates/doc-page.js');
4 |
5 | const docPagesQuery = `
6 | {
7 | docs: allMdx(
8 | filter: {
9 | fields: {
10 | slug: { regex: "/docs/" },
11 | }
12 | }
13 | ) {
14 | edges {
15 | node {
16 | fields {
17 | slug
18 | }
19 | frontmatter {
20 | title
21 | id
22 | }
23 | }
24 | }
25 | }
26 | }
27 | `;
28 |
29 | const createDocPages = (createPage, docPages) => {
30 | const menu = docPages.map((page) => ({
31 | slug: page.node.fields.slug,
32 | ...page.node.frontmatter,
33 | }));
34 |
35 | docPages.forEach((page) => {
36 | createPage({
37 | path: page.node.fields.slug,
38 | component: docsTemplate,
39 | context: {
40 | slug: page.node.fields.slug,
41 | menu,
42 | },
43 | });
44 | });
45 | };
46 |
47 | const blogGenerator = (createPage, graphql) =>
48 | new Promise((resolve, reject) => {
49 | resolve(
50 | graphql(docPagesQuery).then((result) => {
51 | if (result.errors) {
52 | console.log(result.errors);
53 | reject(result.errors);
54 | }
55 |
56 | const docPages = result.data.docs.edges;
57 |
58 | try {
59 | createDocPages(createPage, docPages);
60 | } catch (e) {
61 | console.log(e);
62 | }
63 | })
64 | );
65 | });
66 |
67 | module.exports = blogGenerator;
68 |
--------------------------------------------------------------------------------
/generators/examples.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 |
3 | const exampleTemplate = path.resolve('./src/templates/example-page.js');
4 |
5 | const examplePagesQuery = `{
6 | allExamplesJson {
7 | edges {
8 | node {
9 | slug
10 | title
11 | source
12 | description
13 | }
14 | }
15 | }
16 | }
17 | `;
18 |
19 | const createExamplePage = (createPage, examplePages) => {
20 | examplePages.forEach((page) => {
21 | const { slug, title, source, description } = page.node;
22 |
23 | createPage({
24 | path: slug,
25 | component: exampleTemplate,
26 | context: {
27 | slug,
28 | title,
29 | source,
30 | description,
31 | sourceSlug: `/example-flows/${source}/`,
32 | },
33 | });
34 | });
35 | };
36 |
37 | const exampleGenerator = (createPage, graphql) =>
38 | new Promise((resolve, reject) => {
39 | resolve(
40 | graphql(examplePagesQuery).then((result) => {
41 | if (result.errors) {
42 | console.log(result.errors);
43 | reject(result.errors);
44 | }
45 |
46 | const pages = result.data.allExamplesJson.edges;
47 |
48 | try {
49 | createExamplePage(createPage, pages);
50 | } catch (e) {
51 | console.log(e);
52 | }
53 | })
54 | );
55 | });
56 |
57 | module.exports = exampleGenerator;
58 |
--------------------------------------------------------------------------------
/generators/markdown-pages.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const utils = require('./utils');
3 |
4 | const defaultTemplate = path.resolve('./src/templates/default-page.js');
5 |
6 | const mdQuery = `
7 | {
8 | pages: allMdx {
9 | edges {
10 | node {
11 | fields {
12 | slug
13 | }
14 | frontmatter {
15 | published
16 | }
17 | }
18 | }
19 | }
20 | }
21 | `;
22 |
23 | const filterSpecialPages = (mdPage) => {
24 | const slug = mdPage.node.fields.slug;
25 | return slug.indexOf('/blog') !== 0 && slug.indexOf('/projects') !== 0;
26 | };
27 |
28 | const createMdPages = (createPage, mdPages) => {
29 | mdPages
30 | .filter(filterSpecialPages)
31 | .filter(utils.filterPublished)
32 | .forEach((mdPage) => {
33 | createPage({
34 | path: mdPage.node.fields.slug,
35 | component: defaultTemplate,
36 | context: {
37 | slug: mdPage.node.fields.slug,
38 | fields: mdPage.node.fields,
39 | },
40 | });
41 | });
42 | };
43 |
44 | const mdPagesGenerator = (createPage, graphql) =>
45 | new Promise((resolve, reject) => {
46 | resolve(
47 | graphql(mdQuery).then((result) => {
48 | if (result.errors) {
49 | console.log(result.errors);
50 | reject(result.errors);
51 | }
52 |
53 | const mdPages = result.data.pages.edges;
54 | try {
55 | createMdPages(createPage, mdPages);
56 | } catch (e) {
57 | console.log(e);
58 | }
59 | })
60 | );
61 | });
62 |
63 | module.exports = mdPagesGenerator;
64 |
--------------------------------------------------------------------------------
/generators/utils.js:
--------------------------------------------------------------------------------
1 | function filterPublished(post) {
2 | const isPublished = post.node.frontmatter && post.node.frontmatter.published;
3 | return process.env.NODE_ENV === 'development' || isPublished;
4 | }
5 |
6 | module.exports = {
7 | filterPublished,
8 | };
9 |
--------------------------------------------------------------------------------
/netlify.toml:
--------------------------------------------------------------------------------
1 | [[plugins]]
2 | package = "netlify-plugin-gatsby-cache"
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-flow-website",
3 | "description": "",
4 | "version": "1.0.0",
5 | "author": "webkid",
6 | "bugs": {
7 | "url": "https://github.com/wbkd/react-flow/issues"
8 | },
9 | "browserslist": [
10 | ">0.25%",
11 | "ie 11"
12 | ],
13 | "dependencies": {
14 | "@emotion/react": "^11.4.0",
15 | "@emotion/styled": "^11.3.0",
16 | "@mdx-js/mdx": "^1.6.22",
17 | "@mdx-js/react": "^1.6.22",
18 | "@react-hook/size": "^2.1.2",
19 | "@svgr/webpack": "^5.5.0",
20 | "babel-polyfill": "^6.26.0",
21 | "d3-array": "^3.0.1",
22 | "d3-color": "^3.0.1",
23 | "d3-scale": "^4.0.0",
24 | "d3-shape": "^3.0.1",
25 | "dagre": "^0.8.5",
26 | "emotion-normalize": "^11.0.1",
27 | "gatsby": "^3.8.1",
28 | "gatsby-plugin-draft": "^0.1.0",
29 | "gatsby-plugin-emotion": "^6.8.0",
30 | "gatsby-plugin-image": "^1.8.0",
31 | "gatsby-plugin-manifest": "^3.8.0",
32 | "gatsby-plugin-mdx": "^2.8.0",
33 | "gatsby-plugin-netlify": "^3.8.0",
34 | "gatsby-plugin-react-helmet": "^4.8.0",
35 | "gatsby-plugin-react-svg": "^3.0.1",
36 | "gatsby-plugin-svgr": "^2.1.0",
37 | "gatsby-remark-copy-linked-files": "^4.5.0",
38 | "gatsby-remark-images": "^5.5.0",
39 | "gatsby-remark-unwrap-images": "^1.0.2",
40 | "gatsby-source-filesystem": "^3.8.0",
41 | "gatsby-transformer-javascript-frontmatter": "^3.8.0",
42 | "gatsby-transformer-json": "^3.8.0",
43 | "gatsby-transformer-remark": "^4.5.0",
44 | "localforage": "^1.9.0",
45 | "popmotion": "^10.0.2",
46 | "prism-react-renderer": "^1.2.1",
47 | "rc-table": "^7.17.0",
48 | "react": "^17.0.2",
49 | "react-dom": "^17.0.2",
50 | "react-flow-renderer": "^9.6.9",
51 | "react-helmet": "^6.1.0",
52 | "reflexbox": "^4.0.6",
53 | "remark-unwrap-images": "^2.1.0",
54 | "slugify": "^1.5.3"
55 | },
56 | "devDependencies": {
57 | "@octokit/rest": "^18.6.6",
58 | "babel-preset-gatsby": "^1.8.0",
59 | "eslint": "^7.29.0",
60 | "eslint-plugin-react": "^7.24.0",
61 | "gatsby-cli": "^3.8.0",
62 | "gatsby-plugin-sharp": "^3.8.0",
63 | "gatsby-transformer-sharp": "^3.8.0",
64 | "gh-pages": "^3.2.3",
65 | "prettier": "^2.3.2",
66 | "sharp": "^0.28.3"
67 | },
68 | "homepage": "https://reactflow.dev",
69 | "keywords": [
70 | "gatsby"
71 | ],
72 | "license": "MIT",
73 | "main": "n/a",
74 | "repository": {
75 | "type": "git",
76 | "url": "git+https://github.com/wbkd/react-flow.git"
77 | },
78 | "scripts": {
79 | "start": "npm run dev",
80 | "dev": "gatsby develop -H 0.0.0.0",
81 | "clean": "gatsby clean",
82 | "lint": "eslint --ext .js,.jsx --ignore-pattern public .",
83 | "test": "echo \"Error: no test specified\" && exit 1",
84 | "format": "prettier --trailing-comma es5 --no-semi --single-quote --write 'src/**/*.js' 'src/**/*.md'",
85 | "build": "GATSBY_EXPERIMENTAL_PAGE_BUILD_ON_DATA_CHANGES=true gatsby build --log-pages",
86 | "deploy": "gatsby build --prefix-paths && gh-pages -d public",
87 | "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"
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/src/assets/data/showcases.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "title": "Botfront - Conversational platform",
4 | "url": "https://botfront.io/",
5 | "image": "../images/showcases/botfront-showcase.png"
6 | },
7 | {
8 | "title": "datablocks - Node-based data editor",
9 | "url": "https://datablocks.pro",
10 | "image": "../images/showcases/datablocks-showcase.png"
11 | },
12 | {
13 | "title": "Circles - Visual degree planner",
14 | "url": "https://circles360.github.io/",
15 | "image": "../images/showcases/circles-showcase.png"
16 | }
17 | ]
18 |
--------------------------------------------------------------------------------
/src/assets/icons/arrow_right.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/src/assets/icons/arrow_right_circle.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/icons/close.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/src/assets/icons/code.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/src/assets/icons/eye.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/src/assets/icons/favicon_stroke.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/src/assets/icons/github_circle.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/src/assets/icons/graph_bar.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/icons/info_circle.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/src/assets/icons/lightning.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/icons/link.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/icons/mail.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/icons/menu.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/src/assets/icons/pen.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/icons/phone.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/src/assets/icons/twitter_circle.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/src/assets/icons/twitter_outline.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/src/assets/images/blog-webkid-teaser.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xyflow/react-flow-docs-v9/4ab1ff8e6dfbf94c31868f913f6338d57c83667b/src/assets/images/blog-webkid-teaser.jpg
--------------------------------------------------------------------------------
/src/assets/images/logo-white.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/src/assets/images/logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/src/assets/images/react-flow-logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/src/assets/images/showcases/botfront-showcase.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xyflow/react-flow-docs-v9/4ab1ff8e6dfbf94c31868f913f6338d57c83667b/src/assets/images/showcases/botfront-showcase.png
--------------------------------------------------------------------------------
/src/assets/images/showcases/circles-showcase.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xyflow/react-flow-docs-v9/4ab1ff8e6dfbf94c31868f913f6338d57c83667b/src/assets/images/showcases/circles-showcase.png
--------------------------------------------------------------------------------
/src/assets/images/showcases/datablocks-showcase.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xyflow/react-flow-docs-v9/4ab1ff8e6dfbf94c31868f913f6338d57c83667b/src/assets/images/showcases/datablocks-showcase.png
--------------------------------------------------------------------------------
/src/components/Button/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import styled from '@emotion/styled';
3 | import { rgba } from 'utils/css-utils';
4 | import { Flex } from 'reflexbox';
5 |
6 | import Icon from 'components/Icon';
7 | import { getThemeColor } from 'utils/css-utils';
8 |
9 | const Button = styled.button`
10 | color: ${(p) => p.theme.colors[p.color || 'button']};
11 | border: ${(p) =>
12 | p.type === 'ghost'
13 | ? '1px solid rgba(0,0,0,0)'
14 | : `1px solid ${p.theme.colors[p.color || 'button']}`};
15 | background: ${(p) =>
16 | p.active
17 | ? rgba(p.color ? p.theme.colors[p.color] : p.theme.colors.button, 0.2)
18 | : 'none'};
19 | padding: ${(p) => (p.type === 'big' ? '12px 20px' : '8px 16px')};
20 | border-radius: 25px;
21 | outline: none;
22 | cursor: pointer;
23 | font-size: ${(p) => p.theme.fontSizesPx[1]};
24 | letter-spacing: 0.5px;
25 | line-height: 1;
26 | transition: 0.075s all ease-in-out;
27 |
28 | svg {
29 | width: 100%;
30 | }
31 |
32 | &:visited,
33 | &:focus,
34 | &:active {
35 | color: ${(p) => p.theme.colors[p.color || 'button']};
36 | }
37 |
38 | &:hover {
39 | opacity: 1;
40 | color: ${getThemeColor('background')};
41 | background-color: ${getThemeColor('button')};
42 |
43 | svg {
44 | path,
45 | circle,
46 | rect,
47 | line,
48 | polyline {
49 | stroke: ${getThemeColor('background')};
50 | }
51 | }
52 |
53 | svg {
54 | .nostroke {
55 | stroke: none;
56 | fill: ${getThemeColor('background')};
57 | }
58 | }
59 | }
60 |
61 | &:active {
62 | transform: scale(0.95);
63 | }
64 | `;
65 |
66 | export default ({
67 | icon,
68 | color = 'button',
69 | children,
70 | type = 'normal',
71 | iconWidth = '20px',
72 | ...props
73 | }) => (
74 |
75 |
76 | {icon && (
77 |
84 | )}
85 | {children}
86 |
87 |
88 | );
89 |
--------------------------------------------------------------------------------
/src/components/CenterContent/index.js:
--------------------------------------------------------------------------------
1 | import styled from '@emotion/styled';
2 | import { Box } from 'reflexbox';
3 |
4 | import { getThemeSpacePx } from 'utils/css-utils';
5 |
6 | export default styled(Box)`
7 | max-width: ${(p) =>
8 | p.maxWidth || (p.big ? p.theme.maxWidthBig : p.theme.maxWidth)};
9 | margin-left: auto;
10 | margin-right: auto;
11 | padding-left: ${getThemeSpacePx(3)};
12 | padding-right: ${getThemeSpacePx(3)};
13 | `;
14 |
--------------------------------------------------------------------------------
/src/components/Close/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import styled from '@emotion/styled';
3 | import { Flex } from 'reflexbox';
4 |
5 | import { getThemeSpacePx } from 'utils/css-utils';
6 | import Icon from 'components/Icon';
7 |
8 | const Close = styled(Flex)`
9 | padding: ${getThemeSpacePx(1)};
10 | line-height: 1;
11 | width: 50px;
12 | height: 50px;
13 | `;
14 |
15 | export default ({ onClick, ...props }) => {
16 | return (
17 |
18 |
19 |
20 |
21 |
22 | );
23 | };
24 |
--------------------------------------------------------------------------------
/src/components/CodeBlock/Mdx.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Highlight, { defaultProps } from 'prism-react-renderer';
3 | import dracula from 'prism-react-renderer/themes/github';
4 |
5 | export default ({ children, className = 'javascript', language }) => {
6 | const lang = language ? language : className.replace(/language-/, '');
7 |
8 | return (
9 |
15 | {({ className, style, tokens, getLineProps, getTokenProps }) => (
16 |
27 | {tokens.map(
28 | (line, i, lines) =>
29 | !(
30 | line.length === 1 &&
31 | line[0].empty &&
32 | (i === 0 || i === lines.length - 1)
33 | ) && (
34 |
35 | {line.map((token, key) => (
36 |
37 | ))}
38 |
39 | )
40 | )}
41 |
42 | )}
43 |
44 | );
45 | };
46 |
--------------------------------------------------------------------------------
/src/components/CodeBlock/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import styled from '@emotion/styled';
3 | import { Box } from 'reflexbox';
4 |
5 | import { colors } from 'themes';
6 | import CodeBlock from './Mdx';
7 | import { H4, AttributionText } from 'components/Typo';
8 |
9 | const Wrapper = styled(Box)`
10 | margin: 0 auto;
11 | padding: 5px 0;
12 |
13 | h4 {
14 | margin-bottom: 10px;
15 | }
16 |
17 | pre {
18 | margin: 0;
19 | font-size: 14px;
20 | }
21 |
22 | code {
23 | border: 1px solid ${colors.lightGrey};
24 | border-radius: 3px;
25 | }
26 | `;
27 |
28 | export default ({
29 | language = 'javascript',
30 | code,
31 | title = '',
32 | subtitle = '',
33 | }) => {
34 | return (
35 |
36 | {title && {title} }
37 | {code}
38 | {subtitle && {subtitle} }
39 |
40 | );
41 | };
42 |
--------------------------------------------------------------------------------
/src/components/ContentSection/index.js:
--------------------------------------------------------------------------------
1 | import React, { Fragment } from 'react';
2 | import { Box } from 'reflexbox';
3 |
4 | import theme from 'themes/index';
5 |
6 | import CenterContent from 'components/CenterContent';
7 |
8 | export default ({
9 | children,
10 | bg = 'transparent',
11 | centered = false,
12 | id = null,
13 | big = false,
14 | ...rest
15 | }) => {
16 | const WrapperComponent = centered ? CenterContent : Fragment;
17 | const wrapperProps = centered ? { big: big } : {};
18 |
19 | return (
20 |
26 | {children}
27 |
28 | );
29 | };
30 |
--------------------------------------------------------------------------------
/src/components/Footer/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import styled from '@emotion/styled';
3 | import { Flex } from 'reflexbox';
4 |
5 | import Icon from 'components/Icon';
6 | import Logo from 'components/Logo';
7 |
8 | const Wrapper = styled.footer`
9 | flex-shrink: 0;
10 | border-top: ${(p) =>
11 | p.hasBorder ? `1px solid ${p.theme.colors.silverLighten30}` : 'nonde'};
12 | `;
13 |
14 | const Header = ({ hasBorder = false }) => {
15 | return (
16 |
17 |
18 |
19 | A project by
20 |
21 |
22 |
23 |
24 |
25 |
34 |
42 |
43 |
44 |
45 | );
46 | };
47 |
48 | export default Header;
49 |
--------------------------------------------------------------------------------
/src/components/HeroFlow/ColorPickerNode.js:
--------------------------------------------------------------------------------
1 | import React, { memo, useCallback } from 'react';
2 | import styled from '@emotion/styled';
3 | import { Handle } from 'react-flow-renderer';
4 |
5 | import { getThemeColor } from 'utils/css-utils';
6 |
7 | const ColorPickerNodeWrapper = styled.div`
8 | padding: 10px;
9 | background: white;
10 | border: 1px solid ${getThemeColor('violet')};
11 | border-radius: 4px;
12 | box-shadow: ${(p) =>
13 | p.selected ? `0 0 0 0.25px ${p.theme.colors.violet}` : 'none'};
14 |
15 | .react-flow__handle {
16 | background: ${(p) => p.color};
17 | }
18 |
19 | input {
20 | -webkit-appearance: none;
21 | appearance: none;
22 | width: 100%;
23 | height: 8px;
24 | background: ${getThemeColor('silver')};
25 | outline: none;
26 | border-radius: 2px;
27 | }
28 |
29 | input::-webkit-slider-thumb {
30 | -webkit-appearance: none;
31 | appearance: none;
32 | width: 12px;
33 | height: 12px;
34 | background: ${(p) => p.color};
35 | cursor: pointer;
36 | border-radius: 100%;
37 | }
38 |
39 | input::-moz-range-thumb {
40 | width: 12px;
41 | height: 12px;
42 | background: ${(p) => p.color};
43 | cursor: pointer;
44 | border-radius: 100%;
45 | }
46 | `;
47 |
48 | const ColorPickerNode = memo(({ data, id, selected }) => {
49 | const onChange = useCallback((event) => data.onChange(event, id), [data, id]);
50 | const colorName = `${data.color[0].toUpperCase()}${data.color.substr(1)}`;
51 |
52 | return (
53 |
58 | {colorName} amount
59 |
67 |
68 |
69 | );
70 | });
71 |
72 | export default ColorPickerNode;
73 |
--------------------------------------------------------------------------------
/src/components/Icon/index.js:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from 'react';
2 | import styled from '@emotion/styled';
3 | import { Flex } from 'reflexbox';
4 | import { isOldIE } from 'utils/browser-utils';
5 |
6 | import Mail from 'assets/icons/mail.svg';
7 | import Github from 'assets/icons/github_circle.svg';
8 | import ArrowRight from 'assets/icons/arrow_right.svg';
9 | import Menu from 'assets/icons/menu.svg';
10 | import Code from 'assets/icons/code.svg';
11 |
12 | const IconWrapper = styled(Flex)`
13 | justify-content: center;
14 | align-items: center;
15 |
16 | svg {
17 | max-width: 100%;
18 | height: auto;
19 | display: flex;
20 | flex-direction: column;
21 | justify-content: center;
22 |
23 | path,
24 | circle,
25 | rect,
26 | line,
27 | polyline {
28 | stroke: ${(p) =>
29 | p.colorizeStroke
30 | ? p.theme.colors[p.strokeColor] || p.theme.colors.text
31 | : 'none'};
32 | }
33 |
34 | path.nostroke,
35 | circle.nostroke,
36 | rect.nostroke,
37 | line.nostroke,
38 | polyline.nostroke {
39 | stroke: none;
40 | fill: ${(p) =>
41 | p.colorizeStroke
42 | ? p.theme.colors[p.strokeColor] || p.theme.colors.text
43 | : 'none'};
44 | }
45 | }
46 | `;
47 |
48 | const preLoaded = {
49 | mail: Mail,
50 | arrow_right: ArrowRight,
51 | menu: Menu,
52 | github_circle: Github,
53 | code: Code,
54 | };
55 |
56 | const getPreLoadedIcon = (name) =>
57 | preLoaded[name] ? () => preLoaded[name] : null;
58 |
59 | export default ({
60 | name = 'datavis',
61 | colorizeStroke = false,
62 | strokeColor = null,
63 | ...restProps
64 | }) => {
65 | const [SVGComponent, setSVGComponent] = useState(getPreLoadedIcon(name));
66 |
67 | useEffect(() => {
68 | if (!SVGComponent) {
69 | (async () => {
70 | try {
71 | const svgComp = await import(`assets/icons/${name}.svg`);
72 | setSVGComponent(svgComp);
73 | } catch (err) {
74 | console.warn(`error loading icon: ${name}`);
75 | }
76 | })();
77 | }
78 | }, [SVGComponent, name]);
79 |
80 | if (!SVGComponent) {
81 | return null;
82 | }
83 |
84 | const IconComponent = SVGComponent.default
85 | ? SVGComponent.default
86 | : SVGComponent;
87 |
88 | if (isOldIE()) {
89 | return null;
90 | }
91 |
92 | return (
93 |
100 |
101 |
102 | );
103 | };
104 |
--------------------------------------------------------------------------------
/src/components/InfoBox/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import styled from '@emotion/styled';
3 | import { Box } from 'reflexbox';
4 |
5 | const Wrapper = styled(Box)`
6 | padding: 16px;
7 | background: ${(p) => p.theme.colors.violetLighten45};
8 | color: white;
9 | border-radius: 5px;
10 | margin: 20px 0;
11 | `;
12 |
13 | const Title = styled(Box)`
14 | font-size: 20px;
15 | margin-bottom: 4px;
16 | font-weight: 900;
17 | `;
18 |
19 | export default ({ title = null, text, ...rest }) => {
20 | return (
21 |
22 | {title}
23 | {text}
24 |
25 | );
26 | };
27 |
--------------------------------------------------------------------------------
/src/components/Link/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Link as GatsbyLink } from 'gatsby';
3 |
4 | const Link = ({ children, to, activeClassName, partiallyActive, ...other }) => {
5 | const internal = /^\/(?!\/)/.test(to);
6 |
7 | if (internal) {
8 | return (
9 |
15 | {children}
16 |
17 | );
18 | }
19 | return (
20 |
21 | {children}
22 |
23 | );
24 | };
25 | export default Link;
26 |
--------------------------------------------------------------------------------
/src/components/Logo/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import styled from '@emotion/styled';
3 | import { useTheme, jsx } from '@emotion/react';
4 |
5 | import WbkdLogoWhite from 'assets/images/logo-white.svg';
6 | import WbkdLogoBlack from 'assets/images/logo.svg';
7 |
8 | const Image = styled.img`
9 | display: block;
10 | width: 75px;
11 | `;
12 |
13 | const getLogoSrc = (type, inverted, theme) => {
14 | if (type) {
15 | return type === 'white' ? WbkdLogoWhite : WbkdLogoBlack;
16 | }
17 |
18 | if (inverted) {
19 | return theme.name === 'light' ? WbkdLogoWhite : WbkdLogoBlack;
20 | }
21 |
22 | return theme.name === 'light' ? WbkdLogoBlack : WbkdLogoWhite;
23 | };
24 |
25 | export default ({ type = null, inverted = false, style = {} }) => {
26 | const theme = useTheme();
27 | const logoSrc = getLogoSrc(type, inverted, theme);
28 |
29 | return ;
30 | };
31 |
--------------------------------------------------------------------------------
/src/components/Page/Doc.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import styled from '@emotion/styled';
3 | import { Flex, Box } from 'reflexbox';
4 |
5 | import Page from 'components/Page';
6 | import Sidebar from 'components/Sidebar';
7 |
8 | const Wrapper = styled(Flex)`
9 | border-top: 1px solid ${(p) => p.theme.colors.silverLighten30};
10 | `;
11 |
12 | const DocWrapper = styled(Box)`
13 | max-width: 620px;
14 | margin: 0 auto;
15 | position: relative;
16 | padding: 0 16px;
17 | `;
18 |
19 | export default ({ children, menu = [], ...rest }) => (
20 |
21 |
22 |
23 | {children}
24 |
25 |
26 | );
27 |
--------------------------------------------------------------------------------
/src/components/Page/Example.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import styled from '@emotion/styled';
3 | import { Flex, Box } from 'reflexbox';
4 |
5 | import Page from 'components/Page';
6 | import Sidebar from 'components/Sidebar';
7 | import useExamplePages from 'hooks/useExamplePages';
8 | import CodeBlock from 'components/CodeBlock';
9 | import { Text, H4 } from 'components/Typo';
10 |
11 | import 'example-flows/Overview';
12 |
13 | const Wrapper = styled(Flex)`
14 | flex-grow: 1;
15 | `;
16 |
17 | const ReactFlowWrapper = styled(Box)`
18 | flex-grow: 1;
19 |
20 | .react-flow {
21 | border-bottom: 1px solid ${(p) => p.theme.colors.silverLighten30};
22 | height: 65vh;
23 | }
24 | `;
25 |
26 | const SourceWrapper = styled(Box)`
27 | max-width: 1000px;
28 | margin: 0 auto;
29 | `;
30 |
31 | const SourceCodeBlock = ({ absolutePath, internal }) => {
32 | const splittedPath = absolutePath.split('/');
33 | const fileName = splittedPath[splittedPath.length - 1];
34 |
35 | return (
36 |
37 | {fileName}
38 |
39 |
40 | );
41 | };
42 |
43 | export default ({ children, title, slug, description, sourceCodeFiles = [] }) => {
44 | const menu = useExamplePages();
45 |
46 | const metaTags = {
47 | title: `React Flow - ${title} Example`,
48 | siteUrl: `https://reactflow.dev/examples/${slug}`,
49 | robots: 'index, follow',
50 | };
51 |
52 | const hasSource = sourceCodeFiles.length > 0;
53 |
54 | return (
55 |
56 |
57 |
58 |
59 | {children}
60 | {hasSource && (
61 |
62 | {description && }
63 | {title} Source Code
64 | {sourceCodeFiles.map((source) => (
65 |
66 | ))}
67 |
68 | )}
69 |
70 |
71 |
72 | );
73 | };
74 |
--------------------------------------------------------------------------------
/src/components/Page/MetaTags.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Helmet } from 'react-helmet';
3 |
4 | const MetaTags = ({
5 | title,
6 | description,
7 | siteUrl,
8 | robots,
9 | image,
10 | pathname,
11 | article,
12 | }) => {
13 | if (pathname) {
14 | siteUrl = `${siteUrl}${pathname}`;
15 | }
16 |
17 | if (image) {
18 | image = `https://reactflow.dev${image}`;
19 | } else {
20 | image = 'https://reactflow.dev/images/react-flow-header.jpg';
21 | }
22 |
23 | return (
24 |
25 |
26 |
{title}
27 | {description && }
28 | {description && }
29 |
30 | {siteUrl && }
31 | {(article ? true : null) && }
32 | {title && }
33 | {description && }
34 | {image && }
35 |
36 |
37 | {title && }
38 | {description && }
39 | {image && }
40 |
41 | {process.env.NODE_ENV === 'production' && (
42 |
47 | )}
48 |
49 | );
50 | };
51 |
52 | export default MetaTags;
53 |
--------------------------------------------------------------------------------
/src/components/Page/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import styled from '@emotion/styled';
3 | import { ThemeProvider, jsx } from '@emotion/react';
4 | import { Flex, Box } from 'reflexbox';
5 |
6 | import Header from 'components/Header';
7 | import Footer from 'components/Footer';
8 | import themes from 'themes';
9 |
10 | import NormalizeStyle from 'themes/normalize';
11 | import GlobalStyle from 'themes/global';
12 | import MetaTags from './MetaTags';
13 | import { getThemeColor } from 'utils/css-utils';
14 |
15 | const PageWrapper = styled(Flex)`
16 | color: ${getThemeColor('text')};
17 | width: 100%;
18 | position: relative;
19 | flex-direction: column;
20 | height: 100vh;
21 | position: relative;
22 | `;
23 |
24 | const PageContent = styled(Box)`
25 | flex: 1 0 auto;
26 | `;
27 |
28 | const Page = ({
29 | children,
30 | theme = 'light',
31 | metaTags,
32 | footerBorder = false,
33 | ...rest
34 | }) => {
35 | const pageTheme = themes[theme];
36 |
37 | return (
38 |
39 |
40 |
41 |
42 |
43 |
44 | {children}
45 |
46 |
47 |
48 | );
49 | };
50 |
51 | export default Page;
52 |
--------------------------------------------------------------------------------
/src/components/SectionIntro/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import styled from '@emotion/styled';
3 | import { Box } from 'reflexbox';
4 |
5 | import { H1, H4 } from 'components/Typo';
6 | import { getThemeSpacePx, getThemeColor } from 'utils/css-utils';
7 |
8 | const SectionIntroWrapper = styled(Box)`
9 | text-align: center;
10 | max-width: 750px;
11 | margin: 0 auto;
12 |
13 | ${H1} {
14 | margin: 0 0 ${getThemeSpacePx(3)} 0;
15 | }
16 | `;
17 |
18 | const SectionSubtitle = styled(H4)`
19 | font-weight: 400;
20 | line-height: 1.5;
21 | color: ${getThemeColor('silverDarken30')};
22 | `;
23 |
24 | const SectionIntro = ({ title = '', text = '', ...props }) => {
25 | return (
26 |
27 | {title}
28 | {text && {text} }
29 |
30 | );
31 | };
32 |
33 | export default SectionIntro;
34 |
--------------------------------------------------------------------------------
/src/components/Showcases/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import styled from '@emotion/styled';
3 | import { Flex, Box } from 'reflexbox';
4 | import { GatsbyImage } from 'gatsby-plugin-image';
5 |
6 | import CenterContent from 'components/CenterContent';
7 | import useShowcaseImages from 'hooks/useShowcaseImages';
8 | import { H4 } from 'components/Typo';
9 | import { getThemeColor } from 'utils/css-utils';
10 |
11 | const gridPadding = 3;
12 |
13 | const RoundImage = styled(GatsbyImage)`
14 | border-radius: 4px;
15 | height: 250px;
16 | transition: transform 200ms ease;
17 | `;
18 |
19 | const Title = styled(H4)`
20 | color: ${getThemeColor('textInverted')};
21 | margin: 16px 0 0 0;
22 | font-weight: 400;
23 | `;
24 |
25 | const Link = styled.a`
26 | &&& {
27 | color: ${getThemeColor('silverDarken15')};
28 |
29 | &:hover {
30 | color: ${getThemeColor('silverLighten15')};
31 |
32 | ${RoundImage} {
33 | transform: scale(1.025);
34 | }
35 | }
36 | }
37 | `;
38 |
39 | const Showcases = () => {
40 | const showcases = useShowcaseImages();
41 |
42 | return (
43 |
44 |
45 | {showcases.map((showcase) => (
46 |
52 |
53 |
56 | {showcase.title}
57 |
58 | {showcase.url}
59 |
60 |
61 | ))}
62 |
63 |
64 | );
65 | };
66 |
67 | export default Showcases;
68 |
--------------------------------------------------------------------------------
/src/components/Sidebar/index.js:
--------------------------------------------------------------------------------
1 | import React, { Fragment, useState } from 'react';
2 | import Link from 'gatsby-link';
3 | import styled from '@emotion/styled';
4 | import { Flex } from 'reflexbox';
5 |
6 | import Icon from 'components/Icon';
7 | import Close from 'components/Close';
8 | import useMenuHeight from 'hooks/useMenuHeight';
9 | import { getThemeColor, getThemeSpacePx, device, px } from 'utils/css-utils';
10 |
11 | const Aside = styled.aside`
12 | display: ${(p) => (p.isOpen ? 'block' : 'none')};
13 | position: absolute;
14 | left: 0;
15 | top: 0;
16 | width: 100%;
17 | height: ${(p) => px(p.height)};
18 | overflow-y: auto;
19 | background: white;
20 | z-index: 400;
21 | padding: 20px 10px;
22 |
23 | @media ${device.tablet} {
24 | width: 30%;
25 | max-width: 300px;
26 | padding: 16px;
27 | border-right: 1px solid ${getThemeColor('silverLighten30')};
28 | display: block;
29 | position: relative;
30 | height: auto;
31 |
32 | .mobile {
33 | display: none;
34 | }
35 | }
36 | `;
37 |
38 | const AsideInner = styled.div``;
39 |
40 | const MobileButton = styled(Flex)`
41 | z-index: 10;
42 | background: ${getThemeColor('violetLighten5')};
43 | position: fixed;
44 | right: 16px;
45 | bottom: 16px;
46 | color: white;
47 | border-radius: 100%;
48 | width: 36px;
49 | height: 36px;
50 | cursor: pointer;
51 | justify-content: center;
52 | align-items: center;
53 |
54 | @media ${device.tablet} {
55 | display: none;
56 | }
57 | `;
58 |
59 | const MenuLink = styled(Link)`
60 | display: block;
61 | padding: ${getThemeSpacePx(1)} ${getThemeSpacePx(2)};
62 | margin-left: ${(p) => (p.marginLeft ? `${p.marginLeft}px` : 0)};
63 |
64 | &.active,
65 | &:hover {
66 | background: ${getThemeColor('silverLighten30')};
67 | }
68 | `;
69 |
70 | const GroupLabel = styled.div`
71 | padding: ${getThemeSpacePx(1)} ${getThemeSpacePx(2)};
72 | color: ${getThemeColor('silverDarken60')};
73 | margin-left: ${(p) => (p.marginLeft ? `${p.marginLeft}px` : 0)};
74 | `;
75 |
76 | const MenuItem = ({ title, slug, marginLeft }) => {
77 | return (
78 |
79 | {title}
80 |
81 | );
82 | };
83 |
84 | const SideBarParts = ({ items, level }) =>
85 | items.map((menuItem) => {
86 | if (menuItem.title) {
87 | return (
88 |
89 | );
90 | }
91 |
92 | return (
93 |
94 | {menuItem.group}
95 |
96 |
97 | );
98 | });
99 |
100 | const Sidebar = ({ menu }) => {
101 | const [isOpen, setIsOpen] = useState(false);
102 | const menuHeight = useMenuHeight();
103 |
104 | const toggleSidebar = () => {
105 | const nextMenuOpen = !isOpen;
106 |
107 | document.body.classList.toggle('noscroll', nextMenuOpen);
108 |
109 | setIsOpen(nextMenuOpen);
110 | };
111 |
112 | return (
113 | <>
114 |
115 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 | >
129 | );
130 | };
131 |
132 | export default Sidebar;
133 |
--------------------------------------------------------------------------------
/src/components/TeaserFlow/A.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 | import ReactFlow, {
3 | ReactFlowProvider,
4 | Background,
5 | Controls,
6 | addEdge,
7 | } from 'react-flow-renderer';
8 |
9 | import TeaserFlow from 'components/TeaserFlow';
10 | import { baseColors } from 'themes';
11 |
12 | const initialElements = [
13 | {
14 | id: '1',
15 | type: 'input',
16 | position: {
17 | x: 200,
18 | y: 5,
19 | },
20 | data: {
21 | label: 'Input',
22 | },
23 | },
24 | {
25 | id: '2',
26 | position: {
27 | x: 0,
28 | y: 150,
29 | },
30 | data: {
31 | label: 'Default',
32 | },
33 | },
34 | {
35 | id: '3',
36 | position: {
37 | x: 400,
38 | y: 150,
39 | },
40 | data: {
41 | label: 'Default',
42 | },
43 | },
44 | {
45 | id: '4',
46 | type: 'output',
47 | position: {
48 | x: 200,
49 | y: 300,
50 | },
51 | data: {
52 | label: 'Output',
53 | },
54 | },
55 | {
56 | id: 'e1',
57 | source: '1',
58 | target: '2',
59 | label: 'default edge',
60 | },
61 | {
62 | id: 'e2',
63 | source: '1',
64 | target: '3',
65 | animated: true,
66 | label: 'animated edge',
67 | },
68 | {
69 | id: 'e3',
70 | source: '2',
71 | target: '4',
72 | type: 'smoothstep',
73 | },
74 | {
75 | id: 'e4',
76 | source: '3',
77 | target: '4',
78 | type: 'smoothstep',
79 | },
80 | ];
81 |
82 | const onLoad = (rf) => rf.fitView({ padding: 0.2 });
83 | const hasTouch = typeof window !== 'undefined' && 'ontouchstart' in window;
84 |
85 | export default () => {
86 | const [elements, setElements] = useState(initialElements);
87 | const onConnect = (params) => setElements((els) => addEdge(params, els));
88 |
89 | return (
90 |
94 |
95 |
103 |
104 |
105 |
106 |
107 |
108 | );
109 | };
110 |
--------------------------------------------------------------------------------
/src/components/TeaserFlow/C.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 | import ReactFlow, {
3 | ReactFlowProvider,
4 | Background,
5 | Controls,
6 | addEdge,
7 | MiniMap,
8 | } from 'react-flow-renderer';
9 |
10 | import TeaserFlow from 'components/TeaserFlow';
11 |
12 | const defaultNodeOptions = {
13 | targetPosition: 'left',
14 | sourcePosition: 'right',
15 | style: {
16 | width: 50,
17 | },
18 | };
19 |
20 | const initialElements = [
21 | {
22 | id: 'input',
23 | type: 'input',
24 | position: {
25 | x: 0,
26 | y: 100,
27 | },
28 | data: {
29 | label: 'Input',
30 | },
31 | ...defaultNodeOptions,
32 | },
33 | {
34 | id: 'A',
35 | position: {
36 | x: 150,
37 | y: 0,
38 | },
39 | data: {
40 | label: 'A',
41 | },
42 | ...defaultNodeOptions,
43 | },
44 | {
45 | id: 'B',
46 | position: {
47 | x: 250,
48 | y: 0,
49 | },
50 | data: {
51 | label: 'B',
52 | },
53 | ...defaultNodeOptions,
54 | },
55 | {
56 | id: 'C',
57 | position: {
58 | x: 350,
59 | y: 0,
60 | },
61 | data: {
62 | label: 'C',
63 | },
64 | ...defaultNodeOptions,
65 | },
66 | {
67 | id: 'D',
68 | position: {
69 | x: 150,
70 | y: 200,
71 | },
72 | data: {
73 | label: 'D',
74 | },
75 | ...defaultNodeOptions,
76 | },
77 | {
78 | id: 'E',
79 | position: {
80 | x: 250,
81 | y: 200,
82 | },
83 | data: {
84 | label: 'E',
85 | },
86 | ...defaultNodeOptions,
87 | },
88 | {
89 | id: 'F',
90 | position: {
91 | x: 350,
92 | y: 200,
93 | },
94 | data: {
95 | label: 'F',
96 | },
97 | ...defaultNodeOptions,
98 | },
99 | {
100 | id: 'output',
101 | type: 'output',
102 | position: {
103 | x: 500,
104 | y: 100,
105 | },
106 | data: {
107 | label: 'Output',
108 | },
109 | ...defaultNodeOptions,
110 | },
111 | {
112 | id: 'e1',
113 | source: 'input',
114 | target: 'A',
115 | type: 'step',
116 | },
117 | {
118 | id: 'e2',
119 | source: 'A',
120 | target: 'B',
121 | type: 'step',
122 | },
123 | {
124 | id: 'e3',
125 | source: 'B',
126 | target: 'C',
127 | type: 'step',
128 | },
129 | {
130 | id: 'e4',
131 | source: 'C',
132 | target: 'output',
133 | type: 'step',
134 | },
135 | {
136 | id: 'e5',
137 | source: 'input',
138 | target: 'D',
139 | type: 'step',
140 | animated: true,
141 | },
142 | {
143 | id: 'e6',
144 | source: 'D',
145 | target: 'E',
146 | type: 'step',
147 | animated: true,
148 | },
149 | {
150 | id: 'e7',
151 | source: 'E',
152 | target: 'F',
153 | type: 'step',
154 | animated: true,
155 | },
156 | {
157 | id: 'e8',
158 | source: 'F',
159 | target: 'output',
160 | type: 'step',
161 | animated: true,
162 | },
163 | ];
164 |
165 | const onLoad = (rf) => rf.fitView({ padding: 0.2 });
166 | const hasTouch = typeof window !== 'undefined' && 'ontouchstart' in window;
167 |
168 | export default () => {
169 | const [elements, setElements] = useState(initialElements);
170 | const onConnect = (params) =>
171 | setElements((els) => {
172 | params.type = 'step';
173 | return addEdge(params, els);
174 | });
175 |
176 | return (
177 |
182 |
183 |
192 |
193 |
194 |
195 |
196 |
197 |
198 | );
199 | };
200 |
--------------------------------------------------------------------------------
/src/components/TeaserFlow/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import styled from '@emotion/styled';
3 | import { Flex, Box } from 'reflexbox';
4 | import Link from 'gatsby-link';
5 |
6 | import { H2, Text } from 'components/Typo';
7 | import Icon from 'components/Icon';
8 | import { baseColors } from 'themes';
9 | import { device, getThemeSpacePx } from 'utils/css-utils';
10 |
11 | const Wrapper = styled(Flex)`
12 | justify-content: space-between;
13 | align-items: center;
14 | flex-wrap: wrap;
15 | `;
16 |
17 | const ReactFlowWrapper = styled(Box)`
18 | height: 400px;
19 | background: ${(p) => (p.isDark ? baseColors.violet : 'white')};
20 | border-radius: 10px;
21 | order: 2;
22 | margin: ${getThemeSpacePx(4)} ${getThemeSpacePx(0)};
23 | border: solid rgba(26, 25, 43, 0.054) 1.5px;
24 |
25 | box-shadow: 0 2.8px 2.2px rgba(26, 25, 43, 0.014),
26 | 0 12.5px 10px rgba(26, 25, 43, 0.02),
27 | 0 22.3px 17.9px rgba(26, 25, 43, 0.022),
28 | 0 41.8px 33.4px rgba(26, 25, 43, 0.026), 0 100px 80px rgba(26, 25, 43, 0.02);
29 |
30 | @media ${device.tablet} {
31 | order: ${(p) => p.order};
32 | }
33 |
34 | @media ${device.mobile} {
35 | margin-top: ${getThemeSpacePx(3)};
36 | }
37 |
38 | .react-flow__controls {
39 | opacity: ${(p) => (p.isDark ? 0.5 : 1)};
40 | }
41 | `;
42 |
43 | const DocsLink = styled(Link)`
44 | display: flex;
45 | font-weight: bold;
46 | align-items: center;
47 | margin-top: 16px;
48 |
49 | @media ${device.mobile} {
50 | margin-top: ${getThemeSpacePx(0)};
51 | }
52 |
53 | svg {
54 | transform: translateX(0px);
55 | transition: all 0.125s ease-in-out;
56 | }
57 |
58 | &:hover {
59 | svg {
60 | transform: translateX(5px);
61 | transition: all 0.125s ease-in-out;
62 | }
63 | }
64 | `;
65 |
66 | const DescriptionWrapper = styled(Box)`
67 | order: 1;
68 | margin-bottom: ${getThemeSpacePx(3)};
69 |
70 | @media ${device.tablet} {
71 | order: ${(p) => p.order};
72 | }
73 |
74 | @media ${device.mobile} {
75 | margin-bottom: ${getThemeSpacePx(0)};
76 | }
77 | `;
78 |
79 | const Description = ({ title, description }) => (
80 |
81 | {title}
82 | {description}
83 |
84 | Documentation{' '}
85 |
86 |
87 |
88 | );
89 |
90 | export default ({
91 | title,
92 | description,
93 | textPosition = 'left',
94 | isDark = false,
95 | children,
96 | }) => {
97 | const reactFlowOrder = textPosition === 'left' ? 2 : 1;
98 |
99 | return (
100 |
101 | {textPosition === 'left' && (
102 |
103 | )}
104 |
109 | {children}
110 |
111 | {textPosition !== 'left' && (
112 |
113 | )}
114 |
115 | );
116 | };
117 |
--------------------------------------------------------------------------------
/src/components/Typo/index.js:
--------------------------------------------------------------------------------
1 | import styled from '@emotion/styled';
2 | import { Box } from 'reflexbox';
3 |
4 | import { getThemeSpacePx, getThemeColor, device } from 'utils/css-utils';
5 |
6 | export const H1 = styled.h1`
7 | font-size: ${(p) => p.theme.fontSizesPx[4]};
8 | line-height: 1.1;
9 | letter-spacing: 1px;
10 | font-weight: 900;
11 | margin: ${getThemeSpacePx(3)} 0;
12 |
13 | @media ${device.tablet} {
14 | font-size: ${(p) => p.theme.fontSizesPx[5]};
15 | }
16 | `;
17 |
18 | export const H2 = styled.h2`
19 | font-size: ${(p) => p.theme.fontSizesPx[3]};
20 | line-height: 1.1;
21 | letter-spacing: 1px;
22 | font-weight: 900;
23 | margin: ${getThemeSpacePx(3)} 0;
24 |
25 | @media ${device.tablet} {
26 | font-size: ${(p) => p.theme.fontSizesPx[4]};
27 | }
28 | `;
29 |
30 | export const H3 = styled.h3`
31 | font-size: ${(p) => p.theme.fontSizesPx[2]};
32 | line-height: 1.4;
33 | letter-spacing: 1px;
34 | font-weight: 900;
35 | margin: ${getThemeSpacePx(3)} 0;
36 |
37 | @media ${device.tablet} {
38 | font-size: ${(p) => p.theme.fontSizesPx[3]};
39 | }
40 | `;
41 |
42 | export const H4 = styled.h4`
43 | font-size: ${(p) => p.theme.fontSizesPx[1]};
44 | line-height: 1.3;
45 | letter-spacing: 1px;
46 | font-weight: 900;
47 | margin: ${getThemeSpacePx(2)} 0;
48 |
49 | @media ${device.tablet} {
50 | font-size: ${(p) => p.theme.fontSizesPx[2]};
51 | }
52 | `;
53 |
54 | export const H5 = H4;
55 |
56 | export const UL = styled.ul`
57 | padding: 0;
58 | list-style: none;
59 | `;
60 |
61 | const BulletPointSvg = encodeURIComponent(
62 | ` `
63 | );
64 |
65 | export const LI = styled.li`
66 | color: ${getThemeColor('textLight')};
67 | list-style-image: url('data:image/svg+xml;utf8,${BulletPointSvg}');
68 | `;
69 |
70 | export const Text = styled(Box)`
71 | font-size: ${(p) => p.theme.fontSizesPx[p.fontSize || 1]};
72 | line-height: 1.6;
73 | `;
74 |
75 | export const TextLight = styled(Text)`
76 | color: ${getThemeColor('textLight')};
77 | `;
78 |
79 | export const Label = styled(Box)`
80 | font-size: 12px;
81 | line-height: 1.5;
82 | letter-spacing: 1px;
83 | `;
84 |
85 | export const AttributionText = styled(TextLight)`
86 | text-align: center;
87 | margin-top: 12px;
88 | font-size: ${(p) => p.theme.fontSizesPx[0]};
89 | `;
90 |
91 | export const Paragraph = styled(Box)`
92 | max-width: 520px;
93 | margin-left: auto;
94 | margin-right: auto;
95 | letter-spacing: 0.5px;
96 | line-height: 1.5;
97 |
98 | code {
99 | color: ${(p) => p.theme.colors.violet};
100 | }
101 | `;
102 |
--------------------------------------------------------------------------------
/src/example-flows/ContextualZoomFeatures/ZoomNode.js:
--------------------------------------------------------------------------------
1 | import React, { memo } from 'react';
2 |
3 | import { Handle, useStoreState } from 'react-flow-renderer';
4 |
5 | const Placeholder = () => (
6 |
11 | );
12 |
13 | export default memo(({ data }) => {
14 | const [, , zoom] = useStoreState((state) => state.transform);
15 | const showContent = zoom >= 1.5;
16 |
17 | return (
18 | <>
19 |
20 | {showContent ? data.content : }
21 |
22 | >
23 | );
24 | });
25 |
--------------------------------------------------------------------------------
/src/example-flows/ContextualZoomFeatures/index.css:
--------------------------------------------------------------------------------
1 | .react-flow__node-zoom {
2 | font-size: 12px;
3 | background: #fff;
4 | border: 1px solid #555;
5 | border-radius: 5px;
6 | text-align: center;
7 | width: 150px;
8 | padding: 10px;
9 | line-height: 1.2;
10 | }
11 |
12 | .react-flow__node-zoom img {
13 | pointer-events: none;
14 | }
15 |
16 | .placeholder div {
17 | background: #eee;
18 | width: 100%;
19 | height: 10px;
20 | margin-bottom: 4px;
21 | }
22 |
23 | .placeholder div:last-child {
24 | margin-bottom: 0;
25 | }
26 |
--------------------------------------------------------------------------------
/src/example-flows/ContextualZoomFeatures/index.js:
--------------------------------------------------------------------------------
1 | import React, { useState, useCallback } from 'react';
2 |
3 | import ReactFlow, {
4 | removeElements,
5 | addEdge,
6 | MiniMap,
7 | Controls,
8 | } from 'react-flow-renderer';
9 |
10 | import ZoomNode from './ZoomNode';
11 |
12 | import './index.css';
13 |
14 | const snapGrid = [20, 20];
15 | const nodeTypes = {
16 | zoom: ZoomNode,
17 | };
18 |
19 | const initialElements = [
20 | {
21 | id: '1',
22 | type: 'zoom',
23 | data: {
24 | content: <>Zoom to toggle content and placeholder>,
25 | },
26 | position: { x: 0, y: 50 },
27 | },
28 | {
29 | id: '2',
30 | type: 'zoom',
31 | data: { content: <>this is a node with some lines of text in it.> },
32 | position: { x: 300, y: 50 },
33 | },
34 | {
35 | id: '3',
36 | type: 'zoom',
37 | data: { content: <>this is another node with some more text.> },
38 | position: { x: 650, y: 50 },
39 | },
40 | {
41 | id: 'e1-2',
42 | source: '1',
43 | target: '2',
44 | animated: true,
45 | },
46 | {
47 | id: 'e2-3',
48 | source: '2',
49 | target: '3',
50 | animated: true,
51 | },
52 | ];
53 |
54 | const CustomNodeFlow = () => {
55 | const [elements, setElements] = useState(initialElements);
56 |
57 | const onElementsRemove = useCallback(
58 | (elementsToRemove) =>
59 | setElements((els) => removeElements(elementsToRemove, els)),
60 | []
61 | );
62 | const onConnect = useCallback(
63 | (params) =>
64 | setElements((els) => addEdge({ ...params, animated: true }, els)),
65 | []
66 | );
67 |
68 | return (
69 |
78 |
79 |
80 |
81 | );
82 | };
83 |
84 | export default CustomNodeFlow;
85 |
--------------------------------------------------------------------------------
/src/example-flows/CustomConnectionLine/ConnectionLine.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | export default ({
4 | sourceX,
5 | sourceY,
6 | sourcePosition,
7 | targetX,
8 | targetY,
9 | targetPosition,
10 | connectionLineType,
11 | connectionLineStyle,
12 | }) => {
13 | return (
14 |
15 |
22 |
23 |
24 | );
25 | };
26 |
--------------------------------------------------------------------------------
/src/example-flows/CustomConnectionLine/index.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 | import ReactFlow, {
3 | removeElements,
4 | addEdge,
5 | Background,
6 | } from 'react-flow-renderer';
7 |
8 | import ConnectionLine from './ConnectionLine';
9 |
10 | const initialElements = [
11 | {
12 | id: 'connectionline-1',
13 | type: 'input',
14 | data: { label: 'Node 1' },
15 | position: { x: 250, y: 5 },
16 | },
17 | ];
18 |
19 | const ConnectionLineFlow = () => {
20 | const [elements, setElements] = useState(initialElements);
21 | const onElementsRemove = (elementsToRemove) =>
22 | setElements((els) => removeElements(elementsToRemove, els));
23 | const onConnect = (params) => setElements((els) => addEdge(params, els));
24 |
25 | return (
26 |
32 |
33 |
34 | );
35 | };
36 |
37 | export default ConnectionLineFlow;
38 |
--------------------------------------------------------------------------------
/src/example-flows/CustomNode/ColorSelectorNode.js:
--------------------------------------------------------------------------------
1 | import React, { memo } from 'react';
2 |
3 | import { Handle } from 'react-flow-renderer';
4 |
5 | export default memo(({ data, isConnectable }) => {
6 | return (
7 | <>
8 | console.log('handle onConnect', params)}
13 | isConnectable={isConnectable}
14 | />
15 |
16 | Custom Color Picker Node: {data.color}
17 |
18 |
24 |
31 |
38 | >
39 | );
40 | });
41 |
--------------------------------------------------------------------------------
/src/example-flows/CustomNode/index.css:
--------------------------------------------------------------------------------
1 | .react-flow__node-selectorNode {
2 | font-size: 12px;
3 | background: #eee;
4 | border: 1px solid #555;
5 | border-radius: 5px;
6 | text-align: center;
7 | }
8 |
--------------------------------------------------------------------------------
/src/example-flows/DragHandle/DragHandleNode.tsx:
--------------------------------------------------------------------------------
1 | import React, { memo, FC } from 'react';
2 |
3 | import {
4 | Handle,
5 | Position,
6 | NodeProps,
7 | Connection,
8 | Edge,
9 | } from 'react-flow-renderer';
10 |
11 | const onConnect = (params: Connection | Edge) =>
12 | console.log('handle onConnect', params);
13 |
14 | const labelStyle = {
15 | display: 'flex',
16 | alignItems: 'center',
17 | };
18 |
19 | const dragHandleStyle = {
20 | display: 'inline-block',
21 | width: 25,
22 | height: 25,
23 | backgroundColor: 'teal',
24 | marginLeft: 5,
25 | borderRadius: '50%',
26 | };
27 |
28 | const ColorSelectorNode: FC = () => {
29 | return (
30 | <>
31 |
32 |
33 | Only draggable here →{' '}
34 |
35 |
36 |
37 | >
38 | );
39 | };
40 |
41 | export default memo(ColorSelectorNode);
42 |
--------------------------------------------------------------------------------
/src/example-flows/DragHandle/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactFlow from 'react-flow-renderer';
3 |
4 | import DragHandleNode from './DragHandleNode';
5 |
6 | const nodeTypes = {
7 | dragHandleNode: DragHandleNode,
8 | };
9 |
10 | const elements = [
11 | {
12 | id: '2',
13 | type: 'dragHandleNode',
14 | dragHandle: '.custom-drag-handle',
15 | style: { border: '1px solid #ddd', padding: '20px 40px' },
16 | position: { x: 200, y: 200 },
17 | },
18 | ];
19 |
20 | const DragHandleFlow = () => (
21 |
22 | );
23 |
24 | export default DragHandleFlow;
25 |
--------------------------------------------------------------------------------
/src/example-flows/DragNDrop/Sidebar.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | export default () => {
4 | const onDragStart = (event, nodeType) => {
5 | event.dataTransfer.setData('application/reactflow', nodeType);
6 | event.dataTransfer.effectAllowed = 'move';
7 | };
8 |
9 | return (
10 |
11 | You can drag these nodes to the pane on the right.
12 | onDragStart(event, 'input')} draggable>
13 | Input Node
14 |
15 | onDragStart(event, 'default')} draggable>
16 | Default Node
17 |
18 | onDragStart(event, 'output')} draggable>
19 | Output Node
20 |
21 |
22 | );
23 | };
24 |
--------------------------------------------------------------------------------
/src/example-flows/DragNDrop/dnd.css:
--------------------------------------------------------------------------------
1 | .dndflow {
2 | flex-direction: column;
3 | display: flex;
4 | flex-grow: 1;
5 | }
6 |
7 | .dndflow aside {
8 | border-right: 1px solid #eee;
9 | padding: 15px 10px;
10 | font-size: 12px;
11 | background: #fcfcfc;
12 | }
13 |
14 | .dndflow aside .description {
15 | margin-bottom: 10px;
16 | }
17 |
18 | .dndflow .dndnode {
19 | height: 20px;
20 | padding: 4px;
21 | border: 1px solid #1a192b;
22 | border-radius: 2px;
23 | margin-bottom: 10px;
24 | display: flex;
25 | justify-content: center;
26 | align-items: center;
27 | cursor: grab;
28 | }
29 |
30 | .dndflow .dndnode.input {
31 | border-color: #0041d0;
32 | }
33 |
34 | .dndflow .dndnode.output {
35 | border-color: #ff0072;
36 | }
37 |
38 | .dndflow .reactflow-wrapper {
39 | flex-grow: 1;
40 | height: 100%;
41 | }
42 |
43 | .dndflow .selectall {
44 | margin-top: 10px;
45 | }
46 |
47 | @media screen and (min-width: 768px) {
48 | .dndflow {
49 | flex-direction: row;
50 | }
51 |
52 | .dndflow aside {
53 | width: 20%;
54 | max-width: 250px;
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/example-flows/DragNDrop/index.js:
--------------------------------------------------------------------------------
1 | import React, { useState, useRef } from 'react';
2 | import ReactFlow, {
3 | ReactFlowProvider,
4 | addEdge,
5 | removeElements,
6 | Controls,
7 | } from 'react-flow-renderer';
8 |
9 | import Sidebar from './Sidebar';
10 |
11 | import './dnd.css';
12 |
13 | const initialElements = [
14 | {
15 | id: '1',
16 | type: 'input',
17 | data: { label: 'input node' },
18 | position: { x: 250, y: 5 },
19 | },
20 | ];
21 |
22 | let id = 0;
23 | const getId = () => `dndnode_${id++}`;
24 |
25 | const DnDFlow = () => {
26 | const reactFlowWrapper = useRef(null);
27 | const [reactFlowInstance, setReactFlowInstance] = useState(null);
28 | const [elements, setElements] = useState(initialElements);
29 | const onConnect = (params) => setElements((els) => addEdge(params, els));
30 | const onElementsRemove = (elementsToRemove) =>
31 | setElements((els) => removeElements(elementsToRemove, els));
32 |
33 | const onLoad = (_reactFlowInstance) =>
34 | setReactFlowInstance(_reactFlowInstance);
35 |
36 | const onDragOver = (event) => {
37 | event.preventDefault();
38 | event.dataTransfer.dropEffect = 'move';
39 | };
40 |
41 | const onDrop = (event) => {
42 | event.preventDefault();
43 |
44 | const reactFlowBounds = reactFlowWrapper.current.getBoundingClientRect();
45 | const type = event.dataTransfer.getData('application/reactflow');
46 | const position = reactFlowInstance.project({
47 | x: event.clientX - reactFlowBounds.left,
48 | y: event.clientY - reactFlowBounds.top,
49 | });
50 | const newNode = {
51 | id: getId(),
52 | type,
53 | position,
54 | data: { label: `${type} node` },
55 | };
56 |
57 | setElements((es) => es.concat(newNode));
58 | };
59 |
60 | return (
61 |
62 |
63 |
64 |
72 |
73 |
74 |
75 |
76 |
77 |
78 | );
79 | };
80 |
81 | export default DnDFlow;
82 |
--------------------------------------------------------------------------------
/src/example-flows/EdgeTypes/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Example for checking the different edge types and source and target positions
3 | */
4 | import React, { useState } from 'react';
5 |
6 | import ReactFlow, { removeElements, addEdge, MiniMap, Controls, Background } from 'react-flow-renderer';
7 | import { getElements } from './utils';
8 |
9 | const onLoad = (reactFlowInstance) => {
10 | reactFlowInstance.fitView();
11 | console.log(reactFlowInstance.getElements());
12 | };
13 |
14 | const initialElements = getElements();
15 |
16 | const EdgeTypesFlow = () => {
17 | const [elements, setElements] = useState(initialElements);
18 | const onElementsRemove = (elementsToRemove) => setElements((els) => removeElements(elementsToRemove, els));
19 | const onConnect = (params) => setElements((els) => addEdge(params, els));
20 |
21 | return (
22 |
29 |
30 |
31 |
32 |
33 | );
34 | };
35 |
36 | export default EdgeTypesFlow;
37 |
--------------------------------------------------------------------------------
/src/example-flows/EdgeTypes/utils.js:
--------------------------------------------------------------------------------
1 | const nodeWidth = 80;
2 | const nodeGapWidth = nodeWidth * 2;
3 | const nodeStyle = { width: nodeWidth, fontSize: 11, color: 'white' };
4 |
5 | const sourceTargetPositions = [
6 | { source: 'bottom', target: 'top' },
7 | { source: 'right', target: 'left' },
8 | ];
9 | const nodeColors = [
10 | ['#1e9e99', '#4cb3ac', '#6ec9c0', '#8ddfd4'],
11 | ['#0f4c75', '#1b5d8b', '#276fa1', '#3282b8'],
12 | ];
13 | const edgeTypes = ['default', 'step', 'smoothstep', 'straight'];
14 | const offsets = [
15 | {
16 | x: 0,
17 | y: -nodeGapWidth,
18 | },
19 | {
20 | x: nodeGapWidth,
21 | y: -nodeGapWidth,
22 | },
23 | {
24 | x: nodeGapWidth,
25 | y: 0,
26 | },
27 | {
28 | x: nodeGapWidth,
29 | y: nodeGapWidth,
30 | },
31 | {
32 | x: 0,
33 | y: nodeGapWidth,
34 | },
35 | {
36 | x: -nodeGapWidth,
37 | y: nodeGapWidth,
38 | },
39 | {
40 | x: -nodeGapWidth,
41 | y: 0,
42 | },
43 | {
44 | x: -nodeGapWidth,
45 | y: -nodeGapWidth,
46 | },
47 | ];
48 |
49 | let id = 0;
50 | const getNodeId = () => `edgetypes-${(id++).toString()}`;
51 |
52 | export function getElements() {
53 | const initialElements = [];
54 |
55 | for (
56 | let sourceTargetIndex = 0;
57 | sourceTargetIndex < sourceTargetPositions.length;
58 | sourceTargetIndex++
59 | ) {
60 | const currSourceTargetPos = sourceTargetPositions[sourceTargetIndex];
61 |
62 | for (
63 | let edgeTypeIndex = 0;
64 | edgeTypeIndex < edgeTypes.length;
65 | edgeTypeIndex++
66 | ) {
67 | const currEdgeType = edgeTypes[edgeTypeIndex];
68 |
69 | for (let offsetIndex = 0; offsetIndex < offsets.length; offsetIndex++) {
70 | const currOffset = offsets[offsetIndex];
71 |
72 | const style = {
73 | ...nodeStyle,
74 | background: nodeColors[sourceTargetIndex][edgeTypeIndex],
75 | };
76 | const sourcePosition = {
77 | x: offsetIndex * nodeWidth * 4,
78 | y: edgeTypeIndex * 300 + sourceTargetIndex * edgeTypes.length * 300,
79 | };
80 | const sourceId = getNodeId();
81 | const sourceData = { label: `Source ${sourceId}` };
82 | const sourceNode = {
83 | id: sourceId,
84 | style,
85 | data: sourceData,
86 | position: sourcePosition,
87 | sourcePosition: currSourceTargetPos.source,
88 | targetPosition: currSourceTargetPos.target,
89 | };
90 |
91 | const targetId = getNodeId();
92 | const targetData = { label: `Target ${targetId}` };
93 | const targetPosition = {
94 | x: sourcePosition.x + currOffset.x,
95 | y: sourcePosition.y + currOffset.y,
96 | };
97 | const targetNode = {
98 | id: targetId,
99 | style,
100 | data: targetData,
101 | position: targetPosition,
102 | sourcePosition: currSourceTargetPos.source,
103 | targetPosition: currSourceTargetPos.target,
104 | };
105 |
106 | initialElements.push(sourceNode);
107 | initialElements.push(targetNode);
108 |
109 | initialElements.push({
110 | id: `${sourceId}-${targetId}`,
111 | source: sourceId,
112 | target: targetId,
113 | type: currEdgeType,
114 | });
115 | }
116 | }
117 | }
118 |
119 | return initialElements;
120 | }
121 |
--------------------------------------------------------------------------------
/src/example-flows/EdgeWithButton/ButtonEdge.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {
3 | getBezierPath,
4 | getEdgeCenter,
5 | getMarkerEnd,
6 | } from 'react-flow-renderer';
7 |
8 | import './index.css';
9 |
10 | const foreignObjectSize = 40;
11 |
12 | const onEdgeClick = (evt, id) => {
13 | evt.stopPropagation();
14 | alert(`remove ${id}`);
15 | };
16 |
17 | export default function CustomEdge({
18 | id,
19 | sourceX,
20 | sourceY,
21 | targetX,
22 | targetY,
23 | sourcePosition,
24 | targetPosition,
25 | style = {},
26 | data,
27 | arrowHeadType,
28 | markerEndId,
29 | }) {
30 | const edgePath = getBezierPath({
31 | sourceX,
32 | sourceY,
33 | sourcePosition,
34 | targetX,
35 | targetY,
36 | targetPosition,
37 | });
38 | const markerEnd = getMarkerEnd(arrowHeadType, markerEndId);
39 | const [edgeCenterX, edgeCenterY] = getEdgeCenter({
40 | sourceX,
41 | sourceY,
42 | targetX,
43 | targetY,
44 | });
45 |
46 | return (
47 | <>
48 |
55 |
63 |
64 | onEdgeClick(event, id)}
67 | >
68 | ×
69 |
70 |
71 |
72 | >
73 | );
74 | }
75 |
--------------------------------------------------------------------------------
/src/example-flows/EdgeWithButton/index.css:
--------------------------------------------------------------------------------
1 | .edgebutton {
2 | width: 20px;
3 | height: 20px;
4 | background: #eee;
5 | border: 1px solid #fff;
6 | cursor: pointer;
7 | border-radius: 50%;
8 | font-size: 12px;
9 | line-height: 1;
10 | }
11 |
12 | .edgebutton:hover {
13 | box-shadow: 0 0 6px 2px rgba(0, 0, 0, 0.08);
14 | }
15 |
16 | .edgebutton-foreignobject body {
17 | background: transparent;
18 | width: 40px;
19 | height: 40px;
20 | display: flex;
21 | justify-content: center;
22 | align-items: center;
23 | min-height: 40px;
24 | }
25 |
--------------------------------------------------------------------------------
/src/example-flows/EdgeWithButton/index.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 |
3 | import ReactFlow, {
4 | removeElements,
5 | addEdge,
6 | MiniMap,
7 | Controls,
8 | Background,
9 | } from 'react-flow-renderer';
10 |
11 | import ButtonEdge from './ButtonEdge';
12 |
13 | const onLoad = (reactFlowInstance) => reactFlowInstance.fitView();
14 |
15 | const initialElements = [
16 | {
17 | id: 'ewb-1',
18 | type: 'input',
19 | data: { label: 'Input 1' },
20 | position: { x: 250, y: 0 },
21 | },
22 | { id: 'ewb-2', data: { label: 'Node 2' }, position: { x: 250, y: 300 } },
23 |
24 | {
25 | id: 'edge-1-2',
26 | source: 'ewb-1',
27 | target: 'ewb-2',
28 | type: 'buttonedge',
29 | },
30 | ];
31 |
32 | const edgeTypes = {
33 | buttonedge: ButtonEdge,
34 | };
35 |
36 | const EdgeWithButtonFlow = () => {
37 | const [elements, setElements] = useState(initialElements);
38 | const onElementsRemove = (elementsToRemove) =>
39 | setElements((els) => removeElements(elementsToRemove, els));
40 | const onConnect = (params) =>
41 | setElements((els) => addEdge({ ...params, type: 'buttonedge' }, els));
42 |
43 | return (
44 |
53 |
54 |
55 |
56 |
57 | );
58 | };
59 |
60 | export default EdgeWithButtonFlow;
61 |
--------------------------------------------------------------------------------
/src/example-flows/Edges/CustomEdge.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { getBezierPath, getMarkerEnd } from 'react-flow-renderer';
3 |
4 | export default function CustomEdge({
5 | id,
6 | sourceX,
7 | sourceY,
8 | targetX,
9 | targetY,
10 | sourcePosition,
11 | targetPosition,
12 | style = {},
13 | data,
14 | arrowHeadType,
15 | markerEndId,
16 | }) {
17 | const edgePath = getBezierPath({ sourceX, sourceY, sourcePosition, targetX, targetY, targetPosition });
18 | const markerEnd = getMarkerEnd(arrowHeadType, markerEndId);
19 |
20 | return (
21 | <>
22 |
23 |
24 |
25 | {data.text}
26 |
27 |
28 | >
29 | );
30 | }
31 |
--------------------------------------------------------------------------------
/src/example-flows/Empty/index.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 |
3 | import ReactFlow, {
4 | removeElements,
5 | addEdge,
6 | MiniMap,
7 | Controls,
8 | Background,
9 | } from 'react-flow-renderer';
10 |
11 | const onLoad = (reactFlowInstance) =>
12 | console.log('flow loaded:', reactFlowInstance);
13 | const onElementClick = (event, element) => console.log('click', element);
14 | const onNodeDragStop = (event, node) => console.log('drag stop', node);
15 |
16 | const EmptyFlow = () => {
17 | const [elements, setElements] = useState([]);
18 | const onElementsRemove = (elementsToRemove) =>
19 | setElements((els) => removeElements(elementsToRemove, els));
20 | const onConnect = (params) => setElements((els) => addEdge(params, els));
21 | const addRandomNode = () => {
22 | const nodeId = `nodes-${(elements.length + 1).toString()}`;
23 | const newNode = {
24 | id: nodeId,
25 | data: { label: `Node: ${nodeId}` },
26 | position: {
27 | x: Math.random() * window.innerWidth,
28 | y: Math.random() * window.innerHeight,
29 | },
30 | };
31 | setElements((els) => els.concat(newNode));
32 | };
33 |
34 | return (
35 | onConnect(p)}
41 | onNodeDragStop={onNodeDragStop}
42 | >
43 |
44 |
45 |
46 |
47 |
52 | add node
53 |
54 |
55 | );
56 | };
57 |
58 | export default EmptyFlow;
59 |
--------------------------------------------------------------------------------
/src/example-flows/FloatingEdges/FloatingConnectionLine.tsx:
--------------------------------------------------------------------------------
1 | import { FC } from 'react';
2 | import { getBezierPath, ConnectionLineComponentProps, Node } from 'react-flow-renderer';
3 |
4 | import { getEdgeParams } from './utils';
5 |
6 | const FloatingConnectionLine: FC = ({
7 | targetX,
8 | targetY,
9 | sourcePosition,
10 | targetPosition,
11 | sourceNode,
12 | }) => {
13 | if (!sourceNode) {
14 | return null;
15 | }
16 |
17 | const targetNode = {
18 | id: 'connection-target',
19 | __rf: { width: 1, height: 1, position: { x: targetX, y: targetY } },
20 | } as Node;
21 |
22 | const { sx, sy } = getEdgeParams(sourceNode, targetNode);
23 | const d = getBezierPath({
24 | sourceX: sx,
25 | sourceY: sy,
26 | sourcePosition,
27 | targetPosition,
28 | targetX,
29 | targetY,
30 | });
31 |
32 | return (
33 |
34 |
35 |
36 |
37 | );
38 | };
39 |
40 | export default FloatingConnectionLine;
41 |
--------------------------------------------------------------------------------
/src/example-flows/FloatingEdges/FloatingEdge.tsx:
--------------------------------------------------------------------------------
1 | import { FC, useMemo, CSSProperties } from 'react';
2 | import { EdgeProps, getMarkerEnd, useStoreState, getBezierPath } from 'react-flow-renderer';
3 |
4 | import { getEdgeParams } from './utils';
5 |
6 | const FloatingEdge: FC = ({ id, source, target, arrowHeadType, markerEndId, style }) => {
7 | const nodes = useStoreState((state) => state.nodes);
8 | const markerEnd = getMarkerEnd(arrowHeadType, markerEndId);
9 |
10 | const sourceNode = useMemo(() => nodes.find((n) => n.id === source), [source, nodes]);
11 | const targetNode = useMemo(() => nodes.find((n) => n.id === target), [target, nodes]);
12 |
13 | if (!sourceNode || !targetNode) {
14 | return null;
15 | }
16 |
17 | const { sx, sy, tx, ty, sourcePos, targetPos } = getEdgeParams(sourceNode, targetNode);
18 |
19 | const d = getBezierPath({
20 | sourceX: sx,
21 | sourceY: sy,
22 | sourcePosition: sourcePos,
23 | targetPosition: targetPos,
24 | targetX: tx,
25 | targetY: ty,
26 | });
27 |
28 | return (
29 |
30 |
31 |
32 | );
33 | };
34 |
35 | export default FloatingEdge;
36 |
--------------------------------------------------------------------------------
/src/example-flows/FloatingEdges/index.tsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 |
3 | import ReactFlow, {
4 | removeElements,
5 | addEdge,
6 | Background,
7 | OnLoadParams,
8 | EdgeTypesType,
9 | Elements,
10 | Connection,
11 | Edge,
12 | ArrowHeadType,
13 | } from 'react-flow-renderer';
14 |
15 | import './style.css';
16 |
17 | import FloatingEdge from './FloatingEdge';
18 | import FloatingConnectionLine from './FloatingConnectionLine';
19 | import { createElements } from './utils';
20 |
21 | const onLoad = (reactFlowInstance: OnLoadParams) => reactFlowInstance.fitView();
22 |
23 | const initialElements: Elements = createElements();
24 |
25 | const edgeTypes: EdgeTypesType = {
26 | floating: FloatingEdge,
27 | };
28 |
29 | const NodeAsHandleFlow = () => {
30 | const [elements, setElements] = useState(initialElements);
31 |
32 | const onElementsRemove = (elementsToRemove: Elements) => setElements((els) => removeElements(elementsToRemove, els));
33 |
34 | const onConnect = (params: Connection | Edge) =>
35 | setElements((els) => addEdge({ ...params, type: 'floating', arrowHeadType: ArrowHeadType.Arrow }, els));
36 |
37 | return (
38 |
39 |
47 |
48 |
49 |
50 | );
51 | };
52 |
53 | export default NodeAsHandleFlow;
54 |
--------------------------------------------------------------------------------
/src/example-flows/FloatingEdges/style.css:
--------------------------------------------------------------------------------
1 | .floatingedges {
2 | flex-direction: column;
3 | display: flex;
4 | flex-grow: 1;
5 | }
6 |
7 | .floatingedges .react-flow__handle {
8 | opacity: 0;
9 | }
10 |
--------------------------------------------------------------------------------
/src/example-flows/FloatingEdges/utils.ts:
--------------------------------------------------------------------------------
1 | import { Position, ArrowHeadType, Node, XYPosition } from 'react-flow-renderer';
2 |
3 | // this helper function returns the intersection point
4 | // of the line between the center of the intersectionNode and the target node
5 | function getNodeIntersection(intersectionNode: Node, targetNode: Node): XYPosition {
6 | // https://math.stackexchange.com/questions/1724792/an-algorithm-for-finding-the-intersection-point-between-a-center-of-vision-and-a
7 | const {
8 | width: intersectionNodeWidth,
9 | height: intersectionNodeHeight,
10 | position: intersectionNodePosition,
11 | } = intersectionNode.__rf;
12 | const targetPosition = targetNode.__rf.position;
13 |
14 | const w = intersectionNodeWidth / 2;
15 | const h = intersectionNodeHeight / 2;
16 |
17 | const x2 = intersectionNodePosition.x + w;
18 | const y2 = intersectionNodePosition.y + h;
19 | const x1 = targetPosition.x + w;
20 | const y1 = targetPosition.y + h;
21 |
22 | const xx1 = (x1 - x2) / (2 * w) - (y1 - y2) / (2 * h);
23 | const yy1 = (x1 - x2) / (2 * w) + (y1 - y2) / (2 * h);
24 | const a = 1 / (Math.abs(xx1) + Math.abs(yy1));
25 | const xx3 = a * xx1;
26 | const yy3 = a * yy1;
27 | const x = w * (xx3 + yy3) + x2;
28 | const y = h * (-xx3 + yy3) + y2;
29 |
30 | return { x, y };
31 | }
32 |
33 | // returns the position (top,right,bottom or right) passed node compared to the intersection point
34 | function getEdgePosition(node: Node, intersectionPoint: XYPosition) {
35 | const n = { ...node.__rf.position, ...node.__rf };
36 | const nx = Math.round(n.x);
37 | const ny = Math.round(n.y);
38 | const px = Math.round(intersectionPoint.x);
39 | const py = Math.round(intersectionPoint.y);
40 |
41 | if (px <= nx + 1) {
42 | return Position.Left;
43 | }
44 | if (px >= nx + n.width - 1) {
45 | return Position.Right;
46 | }
47 | if (py <= ny + 1) {
48 | return Position.Top;
49 | }
50 | if (py >= n.y + n.height - 1) {
51 | return Position.Bottom;
52 | }
53 |
54 | return Position.Top;
55 | }
56 |
57 | // returns the parameters (sx, sy, tx, ty, sourcePos, targetPos) you need to create an edge
58 | export function getEdgeParams(source: Node, target: Node) {
59 | const sourceIntersectionPoint = getNodeIntersection(source, target);
60 | const targetIntersectionPoint = getNodeIntersection(target, source);
61 |
62 | const sourcePos = getEdgePosition(source, sourceIntersectionPoint);
63 | const targetPos = getEdgePosition(target, targetIntersectionPoint);
64 |
65 | return {
66 | sx: sourceIntersectionPoint.x,
67 | sy: sourceIntersectionPoint.y,
68 | tx: targetIntersectionPoint.x,
69 | ty: targetIntersectionPoint.y,
70 | sourcePos,
71 | targetPos,
72 | };
73 | }
74 |
75 | export function createElements() {
76 | const elements = [];
77 | const center = { x: window.innerWidth / 2, y: window.innerHeight / 2 };
78 |
79 | elements.push({ id: 'target', data: { label: 'Target' }, position: center });
80 |
81 | for (let i = 0; i < 8; i++) {
82 | const degrees = i * (360 / 8);
83 | const radians = degrees * (Math.PI / 180);
84 | const x = 250 * Math.cos(radians) + center.x;
85 | const y = 250 * Math.sin(radians) + center.y;
86 |
87 | elements.push({ id: `${i}`, data: { label: 'Source' }, position: { x, y } });
88 |
89 | elements.push({
90 | id: `edge-${i}`,
91 | target: 'target',
92 | source: `${i}`,
93 | type: 'floating',
94 | arrowHeadType: ArrowHeadType.Arrow,
95 | });
96 | }
97 |
98 | return elements;
99 | }
100 |
--------------------------------------------------------------------------------
/src/example-flows/Hidden/index.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 |
3 | import ReactFlow, { addEdge, MiniMap, Controls } from 'react-flow-renderer';
4 | import { useEffect } from 'react';
5 |
6 | const initialElements = [
7 | {
8 | id: 'hidden-1',
9 | type: 'input',
10 | data: { label: 'Node 1' },
11 | position: { x: 250, y: 5 },
12 | },
13 | { id: 'hidden-2', data: { label: 'Node 2' }, position: { x: 100, y: 100 } },
14 | { id: 'hidden-3', data: { label: 'Node 3' }, position: { x: 400, y: 100 } },
15 | { id: 'hidden-4', data: { label: 'Node 4' }, position: { x: 400, y: 200 } },
16 | { id: 'hidden-e1-2', source: 'hidden-1', target: 'hidden-2' },
17 | { id: 'hidden-e1-3', source: 'hidden-1', target: 'hidden-3' },
18 | { id: 'hidden-e3-4', source: 'hidden-3', target: 'hidden-4' },
19 | ];
20 |
21 | const HiddenFlow = () => {
22 | const [elements, setElements] = useState(initialElements);
23 | const [isHidden, setIsHidden] = useState(false);
24 | const onConnect = (params) => setElements((els) => addEdge(params, els));
25 |
26 | useEffect(() => {
27 | setElements((els) =>
28 | els.map((e) => {
29 | e.isHidden = isHidden;
30 | return e;
31 | })
32 | );
33 | }, [isHidden]);
34 |
35 | return (
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 | isHidden
44 | setIsHidden(event.target.checked)}
49 | className="react-flow__ishidden"
50 | />
51 |
52 |
53 |
54 |
55 | );
56 | };
57 |
58 | export default HiddenFlow;
59 |
--------------------------------------------------------------------------------
/src/example-flows/Layouting/index.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable react-hooks/exhaustive-deps */
2 | import React, { useState, useCallback } from 'react';
3 | import ReactFlow, {
4 | ReactFlowProvider,
5 | addEdge,
6 | removeElements,
7 | isNode,
8 | } from 'react-flow-renderer';
9 | import dagre from 'dagre';
10 |
11 | import initialElements from './initial-elements';
12 |
13 | import './layouting.css';
14 |
15 | const dagreGraph = new dagre.graphlib.Graph();
16 | dagreGraph.setDefaultEdgeLabel(() => ({}));
17 |
18 | // In order to keep this example simple the node width and height are hardcoded.
19 | // In a real world app you would use the correct width and height values of
20 | // const nodes = useStoreState(state => state.nodes) and then node.__rf.width, node.__rf.height
21 |
22 | const nodeWidth = 172;
23 | const nodeHeight = 36;
24 |
25 | const getLayoutedElements = (elements, direction = 'TB') => {
26 | const isHorizontal = direction === 'LR';
27 | dagreGraph.setGraph({ rankdir: direction });
28 |
29 | elements.forEach((el) => {
30 | if (isNode(el)) {
31 | dagreGraph.setNode(el.id, { width: nodeWidth, height: nodeHeight });
32 | } else {
33 | dagreGraph.setEdge(el.source, el.target);
34 | }
35 | });
36 |
37 | dagre.layout(dagreGraph);
38 |
39 | return elements.map((el) => {
40 | if (isNode(el)) {
41 | const nodeWithPosition = dagreGraph.node(el.id);
42 | el.targetPosition = isHorizontal ? 'left' : 'top';
43 | el.sourcePosition = isHorizontal ? 'right' : 'bottom';
44 |
45 | // unfortunately we need this little hack to pass a slightly different position
46 | // to notify react flow about the change. Moreover we are shifting the dagre node position
47 | // (anchor=center center) to the top left so it matches the react flow node anchor point (top left).
48 | el.position = {
49 | x: nodeWithPosition.x - nodeWidth / 2 + Math.random() / 1000,
50 | y: nodeWithPosition.y - nodeHeight / 2,
51 | };
52 | }
53 |
54 | return el;
55 | });
56 | };
57 |
58 | const layoutedElements = getLayoutedElements(initialElements);
59 |
60 | const LayoutFlow = () => {
61 | const [elements, setElements] = useState(layoutedElements);
62 | const onConnect = (params) =>
63 | setElements((els) =>
64 | addEdge({ ...params, type: 'smoothstep', animated: true }, els)
65 | );
66 | const onElementsRemove = (elementsToRemove) =>
67 | setElements((els) => removeElements(elementsToRemove, els));
68 |
69 | const onLayout = useCallback(
70 | (direction) => {
71 | const layoutedElements = getLayoutedElements(elements, direction);
72 | setElements(layoutedElements);
73 | },
74 | [elements]
75 | );
76 |
77 | return (
78 |
79 |
80 |
86 |
87 | onLayout('TB')}>vertical layout
88 | onLayout('LR')}>horizontal layout
89 |
90 |
91 |
92 | );
93 | };
94 |
95 | export default LayoutFlow;
96 |
--------------------------------------------------------------------------------
/src/example-flows/Layouting/initial-elements.js:
--------------------------------------------------------------------------------
1 | const position = { x: 0, y: 0 };
2 | const edgeType = 'smoothstep';
3 |
4 | export default [
5 | {
6 | id: '1',
7 | type: 'input',
8 | data: { label: 'input' },
9 | position,
10 | },
11 | {
12 | id: '2',
13 | data: { label: 'node 2' },
14 | position,
15 | },
16 | {
17 | id: '2a',
18 | data: { label: 'node 2a' },
19 | position,
20 | },
21 | {
22 | id: '2b',
23 | data: { label: 'node 2b' },
24 | position,
25 | },
26 | {
27 | id: '2c',
28 | data: { label: 'node 2c' },
29 | position,
30 | },
31 | {
32 | id: '2d',
33 | data: { label: 'node 2d' },
34 | position,
35 | },
36 | {
37 | id: '3',
38 | data: { label: 'node 3' },
39 | position,
40 | },
41 | {
42 | id: '4',
43 | data: { label: 'node 4' },
44 | position,
45 | },
46 | {
47 | id: '5',
48 | data: { label: 'node 5' },
49 | position,
50 | },
51 | {
52 | id: '6',
53 | type: 'output',
54 | data: { label: 'output' },
55 | position,
56 | },
57 | { id: '7', type: 'output', data: { label: 'output' }, position },
58 | { id: 'e12', source: '1', target: '2', type: edgeType, animated: true },
59 | { id: 'e13', source: '1', target: '3', type: edgeType, animated: true },
60 | { id: 'e22a', source: '2', target: '2a', type: edgeType, animated: true },
61 | { id: 'e22b', source: '2', target: '2b', type: edgeType, animated: true },
62 | { id: 'e22c', source: '2', target: '2c', type: edgeType, animated: true },
63 | { id: 'e2c2d', source: '2c', target: '2d', type: edgeType, animated: true },
64 | { id: 'e45', source: '4', target: '5', type: edgeType, animated: true },
65 | { id: 'e56', source: '5', target: '6', type: edgeType, animated: true },
66 | { id: 'e57', source: '5', target: '7', type: edgeType, animated: true },
67 | ];
68 |
--------------------------------------------------------------------------------
/src/example-flows/Layouting/layouting.css:
--------------------------------------------------------------------------------
1 | .layoutflow {
2 | flex-grow: 1;
3 | position: relative;
4 | }
5 |
6 | .layoutflow .controls {
7 | position: absolute;
8 | right: 10px;
9 | top: 10px;
10 | z-index: 10;
11 | font-size: 12px;
12 | }
13 |
14 | .layoutflow .controls button:first-child {
15 | margin-right: 10px;
16 | }
17 |
--------------------------------------------------------------------------------
/src/example-flows/Overview/index.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 |
3 | import ReactFlow, {
4 | removeElements,
5 | addEdge,
6 | MiniMap,
7 | Controls,
8 | Background,
9 | } from 'react-flow-renderer';
10 |
11 | import initialElements from './initial-elements';
12 |
13 | const onLoad = (reactFlowInstance) => {
14 | console.log('flow loaded:', reactFlowInstance);
15 | reactFlowInstance.fitView();
16 | };
17 |
18 | const OverviewFlow = () => {
19 | const [elements, setElements] = useState(initialElements);
20 | const onElementsRemove = (elementsToRemove) =>
21 | setElements((els) => removeElements(elementsToRemove, els));
22 | const onConnect = (params) => setElements((els) => addEdge(params, els));
23 |
24 | return (
25 |
33 | {
35 | if (n.style?.background) return n.style.background;
36 | if (n.type === 'input') return '#0041d0';
37 | if (n.type === 'output') return '#ff0072';
38 | if (n.type === 'default') return '#1a192b';
39 |
40 | return '#eee';
41 | }}
42 | nodeColor={(n) => {
43 | if (n.style?.background) return n.style.background;
44 |
45 | return '#fff';
46 | }}
47 | nodeBorderRadius={2}
48 | />
49 |
50 |
51 |
52 | );
53 | };
54 |
55 | export default OverviewFlow;
56 |
--------------------------------------------------------------------------------
/src/example-flows/Overview/initial-elements.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | export default [
4 | {
5 | id: '1',
6 | type: 'input',
7 | data: {
8 | label: (
9 | <>
10 | Welcome to React Flow!
11 | >
12 | ),
13 | },
14 | position: { x: 250, y: 0 },
15 | },
16 | {
17 | id: '2',
18 | data: {
19 | label: (
20 | <>
21 | This is a default node
22 | >
23 | ),
24 | },
25 | position: { x: 100, y: 100 },
26 | },
27 | {
28 | id: '3',
29 | data: {
30 | label: (
31 | <>
32 | This one has a custom style
33 | >
34 | ),
35 | },
36 | position: { x: 400, y: 100 },
37 | style: {
38 | background: '#D6D5E6',
39 | color: '#333',
40 | border: '1px solid #222138',
41 | width: 180,
42 | },
43 | },
44 | {
45 | id: '4',
46 | position: { x: 250, y: 200 },
47 | data: {
48 | label: 'Another default node',
49 | },
50 | },
51 | {
52 | id: '5',
53 | data: {
54 | label: 'Node id: 5',
55 | },
56 | position: { x: 250, y: 325 },
57 | },
58 | {
59 | id: '6',
60 | type: 'output',
61 | data: {
62 | label: (
63 | <>
64 | An output node
65 | >
66 | ),
67 | },
68 | position: { x: 100, y: 480 },
69 | },
70 | {
71 | id: '7',
72 | type: 'output',
73 | data: { label: 'Another output node' },
74 | position: { x: 400, y: 450 },
75 | },
76 | { id: 'e1-2', source: '1', target: '2', label: 'this is an edge label' },
77 | { id: 'e1-3', source: '1', target: '3' },
78 | {
79 | id: 'e3-4',
80 | source: '3',
81 | target: '4',
82 | animated: true,
83 | label: 'animated edge',
84 | },
85 | {
86 | id: 'e4-5',
87 | source: '4',
88 | target: '5',
89 | arrowHeadType: 'arrowclosed',
90 | label: 'edge with arrow head',
91 | },
92 | {
93 | id: 'e5-6',
94 | source: '5',
95 | target: '6',
96 | type: 'smoothstep',
97 | label: 'smooth step edge',
98 | },
99 | {
100 | id: 'e5-7',
101 | source: '5',
102 | target: '7',
103 | type: 'step',
104 | style: { stroke: '#f6ab6c' },
105 | label: 'a step edge',
106 | animated: true,
107 | labelStyle: { fill: '#f6ab6c', fontWeight: 700 },
108 | },
109 | ];
110 |
--------------------------------------------------------------------------------
/src/example-flows/Provider/Sidebar.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { useStoreState, useStoreActions } from 'react-flow-renderer';
3 |
4 | export default () => {
5 | const nodes = useStoreState((store) => store.nodes);
6 | const transform = useStoreState((store) => store.transform);
7 | const setSelectedElements = useStoreActions((actions) => actions.setSelectedElements);
8 |
9 | const selectAll = () => {
10 | setSelectedElements(nodes.map((node) => ({ id: node.id, type: node.type })));
11 | };
12 |
13 | return (
14 |
15 |
16 | This is an example of how you can access the internal state outside of the ReactFlow component.
17 |
18 | Zoom & pan transform
19 |
20 | [{transform[0].toFixed(2)}, {transform[1].toFixed(2)}, {transform[2].toFixed(2)}]
21 |
22 | Nodes
23 | {nodes.map((node) => (
24 |
25 | Node {node.id} - x: {node.__rf.position.x.toFixed(2)}, y: {node.__rf.position.y.toFixed(2)}
26 |
27 | ))}
28 |
29 |
30 | select all nodes
31 |
32 |
33 | );
34 | };
35 |
--------------------------------------------------------------------------------
/src/example-flows/Provider/index.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 | import ReactFlow, {
3 | ReactFlowProvider,
4 | addEdge,
5 | removeElements,
6 | Controls,
7 | } from 'react-flow-renderer';
8 |
9 | import Sidebar from './Sidebar';
10 |
11 | import './provider.css';
12 |
13 | const onElementClick = (event, element) => console.log('click', element);
14 | const onLoad = (reactFlowInstance) =>
15 | console.log('flow loaded:', reactFlowInstance);
16 |
17 | const initialElements = [
18 | {
19 | id: 'provider-1',
20 | type: 'input',
21 | data: { label: 'Node 1' },
22 | position: { x: 250, y: 5 },
23 | },
24 | { id: 'provider-2', data: { label: 'Node 2' }, position: { x: 100, y: 100 } },
25 | { id: 'provider-3', data: { label: 'Node 3' }, position: { x: 400, y: 100 } },
26 | { id: 'provider-4', data: { label: 'Node 4' }, position: { x: 400, y: 200 } },
27 | {
28 | id: 'provider-e1-2',
29 | source: 'provider-1',
30 | target: 'provider-2',
31 | animated: true,
32 | },
33 | { id: 'provider-e1-3', source: 'provider-1', target: 'provider-3' },
34 | ];
35 |
36 | const ProviderFlow = () => {
37 | const [elements, setElements] = useState(initialElements);
38 | const onConnect = (params) => setElements((els) => addEdge(params, els));
39 | const onElementsRemove = (elementsToRemove) =>
40 | setElements((els) => removeElements(elementsToRemove, els));
41 |
42 | return (
43 |
44 |
45 |
46 |
53 |
54 |
55 |
56 |
57 |
58 |
59 | );
60 | };
61 |
62 | export default ProviderFlow;
63 |
--------------------------------------------------------------------------------
/src/example-flows/Provider/provider.css:
--------------------------------------------------------------------------------
1 | .providerflow {
2 | flex-direction: column;
3 | display: flex;
4 | flex-grow: 1;
5 | }
6 |
7 | .providerflow aside {
8 | border-left: 1px solid #eee;
9 | padding: 15px 10px;
10 | font-size: 12px;
11 | background: #fff;
12 | }
13 |
14 | .providerflow aside .description {
15 | margin-bottom: 10px;
16 | }
17 |
18 | .providerflow aside .title {
19 | font-weight: 700;
20 | margin-bottom: 5px;
21 | }
22 |
23 | .providerflow aside .transform {
24 | margin-bottom: 20px;
25 | }
26 |
27 | .providerflow .reactflow-wrapper {
28 | flex-grow: 1;
29 | }
30 |
31 | .providerflow .selectall {
32 | margin-top: 10px;
33 | }
34 |
35 | @media screen and (min-width: 768px) {
36 | .providerflow {
37 | flex-direction: row;
38 | }
39 |
40 | .providerflow aside {
41 | width: 20%;
42 | max-width: 250px;
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/example-flows/SaveRestore/index.js:
--------------------------------------------------------------------------------
1 | import React, { useState, useCallback } from 'react';
2 | import ReactFlow, {
3 | ReactFlowProvider,
4 | removeElements,
5 | addEdge,
6 | useZoomPanHelper,
7 | } from 'react-flow-renderer';
8 | import localforage from 'localforage';
9 |
10 | import './save.css';
11 |
12 | localforage.config({
13 | name: 'react-flow-docs',
14 | storeName: 'flows',
15 | });
16 |
17 | const flowKey = 'example-flow';
18 |
19 | const getNodeId = () => `randomnode_${+new Date()}`;
20 |
21 | const initialElements = [
22 | { id: '1', data: { label: 'Node 1' }, position: { x: 100, y: 100 } },
23 | { id: '2', data: { label: 'Node 2' }, position: { x: 100, y: 200 } },
24 | { id: 'e1-2', source: '1', target: '2' },
25 | ];
26 |
27 | const SaveRestore = () => {
28 | const [rfInstance, setRfInstance] = useState(null);
29 | const [elements, setElements] = useState(initialElements);
30 | const onElementsRemove = (elementsToRemove) =>
31 | setElements((els) => removeElements(elementsToRemove, els));
32 | const onConnect = (params) => setElements((els) => addEdge(params, els));
33 |
34 | const { transform } = useZoomPanHelper();
35 |
36 | const onSave = useCallback(() => {
37 | if (rfInstance) {
38 | const flow = rfInstance.toObject();
39 | localforage.setItem(flowKey, flow);
40 | }
41 | }, [rfInstance]);
42 |
43 | const onRestore = useCallback(() => {
44 | const restoreFlow = async () => {
45 | const flow = await localforage.getItem(flowKey);
46 |
47 | if (flow) {
48 | const [x = 0, y = 0] = flow.position;
49 | setElements(flow.elements || []);
50 | transform({ x, y, zoom: flow.zoom || 0 });
51 | }
52 | };
53 |
54 | restoreFlow();
55 | }, [setElements, transform]);
56 |
57 | const onAdd = useCallback(() => {
58 | const newNode = {
59 | id: getNodeId(),
60 | data: { label: 'Added node' },
61 | position: {
62 | x: Math.random() * window.innerWidth - 100,
63 | y: Math.random() * window.innerHeight,
64 | },
65 | };
66 | setElements((els) => els.concat(newNode));
67 | }, [setElements]);
68 |
69 | return (
70 |
71 |
77 |
78 | save
79 | restore
80 | add node
81 |
82 |
83 |
84 | );
85 | };
86 |
87 | export default SaveRestore;
88 |
--------------------------------------------------------------------------------
/src/example-flows/SaveRestore/save.css:
--------------------------------------------------------------------------------
1 | .save__controls {
2 | position: absolute;
3 | right: 10px;
4 | top: 10px;
5 | z-index: 4;
6 | font-size: 12px;
7 | }
8 |
9 | .save__controls button {
10 | margin-left: 5px;
11 | }
12 |
--------------------------------------------------------------------------------
/src/example-flows/SmoothTransition/index.js:
--------------------------------------------------------------------------------
1 | import { animate } from 'popmotion';
2 | import React, { useCallback, useState } from 'react';
3 | import ReactFlow, { addEdge, Background } from 'react-flow-renderer';
4 |
5 | import './transition.css';
6 |
7 | const initialElements = [
8 | {
9 | id: '1',
10 | type: 'input',
11 | data: { label: 'Smooth Transition' },
12 | position: { x: 250, y: 5 },
13 | },
14 | {
15 | id: '2',
16 | type: 'output',
17 | data: { label: 'zoom-in' },
18 | position: { x: 100, y: 100 },
19 | },
20 | { id: '3', data: { label: 'zoom-out' }, position: { x: 400, y: 100 } },
21 | { id: 'e1-2', source: '1', target: '2' },
22 | { id: 'e1-3', source: '1', target: '3' },
23 | ];
24 |
25 | const SmoothTransition = () => {
26 | const [rfInstance, setRfInstance] = useState(null);
27 | const [elements, setElements] = useState(initialElements);
28 | const onConnect = (params) => setElements((els) => addEdge(params, els));
29 |
30 | const onLoad = useCallback((instance) => {
31 | instance.fitView();
32 | setRfInstance(instance);
33 | }, []);
34 |
35 | const handleZoom = useCallback(
36 | (ratio) => () => {
37 | const { zoom } = rfInstance.toObject();
38 |
39 | animate({
40 | from: zoom,
41 | to: zoom * ratio,
42 | onUpdate: (nextZoom) => rfInstance.zoomTo(nextZoom),
43 | });
44 | },
45 | [rfInstance]
46 | );
47 |
48 | const handleTransform = useCallback(
49 | (transform) => () => {
50 | const {
51 | position: [x, y],
52 | zoom,
53 | } = rfInstance.toObject();
54 |
55 | animate({
56 | from: { x: x, y: y, zoom },
57 | to: transform,
58 | onUpdate: ({ x, y, zoom }) => rfInstance.setTransform({ x, y, zoom }),
59 | });
60 | },
61 | [rfInstance]
62 | );
63 |
64 | return (
65 |
66 |
67 |
68 | zoom in
69 | zoom out
70 | pan to center(0,0,1)
71 |
72 |
73 |
74 |
75 | );
76 | };
77 |
78 | export default SmoothTransition;
79 |
--------------------------------------------------------------------------------
/src/example-flows/SmoothTransition/transition.css:
--------------------------------------------------------------------------------
1 | .transition {
2 | flex-grow: 1;
3 | position: relative;
4 | }
5 |
6 | .transition .controls {
7 | position: absolute;
8 | right: 10px;
9 | top: 10px;
10 | z-index: 10;
11 | font-size: 12px;
12 | }
13 |
14 | .transition .controls button + button {
15 | margin-left: 10px;
16 | }
17 |
--------------------------------------------------------------------------------
/src/example-flows/Stress/index.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 |
3 | import ReactFlow, {
4 | removeElements,
5 | addEdge,
6 | MiniMap,
7 | isNode,
8 | Controls,
9 | Background,
10 | } from 'react-flow-renderer';
11 | import { getElements } from './utils';
12 |
13 | const onLoad = (reactFlowInstance) => {
14 | reactFlowInstance.fitView();
15 | console.log(reactFlowInstance.getElements());
16 | };
17 |
18 | const initialElements = getElements(10, 10);
19 |
20 | const StressFlow = () => {
21 | const [elements, setElements] = useState(initialElements);
22 | const onElementsRemove = (elementsToRemove) =>
23 | setElements((els) => removeElements(elementsToRemove, els));
24 | const onConnect = (params) => setElements((els) => addEdge(params, els));
25 |
26 | const updatePos = () => {
27 | setElements((elms) => {
28 | return elms.map((el) => {
29 | if (isNode(el)) {
30 | return {
31 | ...el,
32 | position: {
33 | x: Math.random() * window.innerWidth,
34 | y: Math.random() * window.innerHeight,
35 | },
36 | };
37 | }
38 |
39 | return el;
40 | });
41 | });
42 | };
43 |
44 | return (
45 |
51 |
52 |
53 |
54 |
55 |
59 | change pos
60 |
61 |
62 | );
63 | };
64 |
65 | export default StressFlow;
66 |
--------------------------------------------------------------------------------
/src/example-flows/Stress/utils.js:
--------------------------------------------------------------------------------
1 | export function getElements(xElements = 10, yElements = 10) {
2 | const initialElements = [];
3 | let nodeId = 1;
4 | let recentNodeId = null;
5 |
6 | for (let y = 0; y < yElements; y++) {
7 | for (let x = 0; x < xElements; x++) {
8 | const position = { x: x * 100, y: y * 50 };
9 | const data = { label: `Node ${nodeId}` };
10 | const node = {
11 | id: `stress-${nodeId.toString()}`,
12 | style: { width: 50, fontSize: 11 },
13 | data,
14 | position,
15 | };
16 | initialElements.push(node);
17 |
18 | if (recentNodeId && nodeId <= xElements * yElements) {
19 | initialElements.push({
20 | id: `${x}-${y}`,
21 | source: `stress-${recentNodeId.toString()}`,
22 | target: `stress-${nodeId.toString()}`,
23 | });
24 | }
25 |
26 | recentNodeId = nodeId;
27 | nodeId++;
28 | }
29 | }
30 |
31 | return initialElements;
32 | }
33 |
--------------------------------------------------------------------------------
/src/example-flows/UpdatableEdge/index.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 | import ReactFlow, { Controls, updateEdge, addEdge } from 'react-flow-renderer';
3 |
4 | const initialElements = [
5 | {
6 | id: '1',
7 | type: 'input',
8 | data: { label: 'Node A' },
9 | position: { x: 250, y: 0 },
10 | },
11 | {
12 | id: '2',
13 | data: { label: 'Node B' },
14 | position: { x: 100, y: 200 },
15 | },
16 | {
17 | id: '3',
18 | data: { label: 'Node C' },
19 | position: { x: 400, y: 200 },
20 | },
21 | { id: 'e1-2', source: '1', target: '2', label: 'updatable edge' },
22 | ];
23 |
24 | const onLoad = (reactFlowInstance) => reactFlowInstance.fitView();
25 |
26 | const UpdatableEdge = () => {
27 | const [elements, setElements] = useState(initialElements);
28 |
29 | // gets called after end of edge gets dragged to another source or target
30 | const onEdgeUpdate = (oldEdge, newConnection) =>
31 | setElements((els) => updateEdge(oldEdge, newConnection, els));
32 | const onConnect = (params) => setElements((els) => addEdge(params, els));
33 |
34 | return (
35 |
42 |
43 |
44 | );
45 | };
46 |
47 | export default UpdatableEdge;
48 |
--------------------------------------------------------------------------------
/src/example-flows/UpdateNode/index.js:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useState } from 'react';
2 | import ReactFlow from 'react-flow-renderer';
3 |
4 | import './updatenode.css';
5 |
6 | const initialElements = [
7 | { id: '1', data: { label: '-' }, position: { x: 100, y: 100 } },
8 | { id: '2', data: { label: 'Node 2' }, position: { x: 100, y: 200 } },
9 | { id: 'e1-2', source: '1', target: '2' },
10 | ];
11 |
12 | const UpdateNode = () => {
13 | const [elements, setElements] = useState(initialElements);
14 | const [nodeName, setNodeName] = useState('Node 1');
15 | const [nodeBg, setNodeBg] = useState('#eee');
16 | const [nodeHidden, setNodeHidden] = useState(false);
17 |
18 | useEffect(() => {
19 | setElements((els) =>
20 | els.map((el) => {
21 | if (el.id === '1') {
22 | // it's important that you create a new object here
23 | // in order to notify react flow about the change
24 | el.data = {
25 | ...el.data,
26 | label: nodeName,
27 | };
28 | }
29 |
30 | return el;
31 | })
32 | );
33 | }, [nodeName, setElements]);
34 |
35 | useEffect(() => {
36 | setElements((els) =>
37 | els.map((el) => {
38 | if (el.id === '1') {
39 | // it's important that you create a new object here
40 | // in order to notify react flow about the change
41 | el.style = { ...el.style, backgroundColor: nodeBg };
42 | }
43 |
44 | return el;
45 | })
46 | );
47 | }, [nodeBg, setElements]);
48 |
49 | useEffect(() => {
50 | setElements((els) =>
51 | els.map((el) => {
52 | if (el.id === '1' || el.id === 'e1-2') {
53 | // when you update a simple type you can just update the value
54 | el.isHidden = nodeHidden;
55 | }
56 |
57 | return el;
58 | })
59 | );
60 | }, [nodeHidden, setElements]);
61 |
62 | return (
63 |
64 |
83 |
84 | );
85 | };
86 |
87 | export default UpdateNode;
88 |
--------------------------------------------------------------------------------
/src/example-flows/UpdateNode/updatenode.css:
--------------------------------------------------------------------------------
1 | .updatenode__controls {
2 | position: absolute;
3 | right: 10px;
4 | top: 10px;
5 | z-index: 4;
6 | font-size: 12px;
7 | }
8 |
9 | .updatenode__controls label {
10 | display: block;
11 | }
12 |
13 | .updatenode__bglabel {
14 | margin-top: 10px;
15 | }
16 |
17 | .updatenode__checkboxwrapper {
18 | margin-top: 10px;
19 | display: flex;
20 | align-items: center;
21 | }
22 |
--------------------------------------------------------------------------------
/src/example-flows/Validation/index.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 |
3 | import ReactFlow, { addEdge, Handle } from 'react-flow-renderer';
4 |
5 | import './validation.css';
6 |
7 | const initialElements = [
8 | { id: '0', type: 'custominput', position: { x: 0, y: 150 } },
9 | { id: 'A', type: 'customnode', position: { x: 250, y: 0 } },
10 | { id: 'B', type: 'customnode', position: { x: 250, y: 150 } },
11 | { id: 'C', type: 'customnode', position: { x: 250, y: 300 } },
12 | ];
13 |
14 | const onLoad = (reactFlowInstance) => reactFlowInstance.fitView();
15 | const isValidConnection = (connection) => connection.target === 'B';
16 | const onConnectStart = (event, { nodeId, handleType }) => console.log('on connect start', { nodeId, handleType });
17 | const onConnectStop = (event) => console.log('on connect stop', event);
18 | const onConnectEnd = (event) => console.log('on connect end', event);
19 |
20 | const CustomInput = () => (
21 | <>
22 | Only connectable with B
23 |
24 | >
25 | );
26 |
27 | const CustomNode = ({ id }) => (
28 | <>
29 |
30 | {id}
31 |
32 | >
33 | );
34 |
35 | const nodeTypes = {
36 | custominput: CustomInput,
37 | customnode: CustomNode,
38 | };
39 |
40 | const HorizontalFlow = () => {
41 | const [elements, setElements] = useState(initialElements);
42 | const onConnect = (params) => {
43 | console.log('on connect', params);
44 | setElements((els) => addEdge(params, els));
45 | };
46 |
47 | return (
48 |
59 | );
60 | };
61 |
62 | export default HorizontalFlow;
63 |
--------------------------------------------------------------------------------
/src/example-flows/Validation/validation.css:
--------------------------------------------------------------------------------
1 | .validationflow .react-flow__node {
2 | width: 150px;
3 | border-radius: 5px;
4 | padding: 10px;
5 | color: #555;
6 | border: 1px solid #ddd;
7 | text-align: center;
8 | font-size: 12px;
9 | }
10 |
11 | .validationflow .react-flow__node-customnode {
12 | background: #e6e6e9;
13 | border: 1px solid #ddd;
14 | }
15 |
16 | .react-flow__node-custominput .react-flow__handle {
17 | background: #e6e6e9;
18 | }
19 |
20 | .validationflow .react-flow__node-custominput {
21 | background: #fff;
22 |
23 | }
24 |
25 | .validationflow .react-flow__handle-connecting {
26 | background: #ff6060;
27 | }
28 |
29 | .validationflow .react-flow__handle-valid {
30 | background: #55dd99;
31 | }
--------------------------------------------------------------------------------
/src/example-flows/ZoomPanHelper/Sidebar.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { useStore, useZoomPanHelper } from 'react-flow-renderer';
3 |
4 | export default () => {
5 | const store = useStore();
6 | const { zoomIn, zoomOut, setCenter } = useZoomPanHelper();
7 |
8 | const focusNode = () => {
9 | const { nodes } = store.getState();
10 |
11 | if (nodes.length) {
12 | const node = nodes[0];
13 |
14 | const x = node.__rf.position.x + node.__rf.width / 2;
15 | const y = node.__rf.position.y + node.__rf.height / 2;
16 | const zoom = 1.85;
17 |
18 | setCenter(x, y, zoom);
19 | }
20 | };
21 |
22 | return (
23 |
31 | );
32 | };
33 |
--------------------------------------------------------------------------------
/src/example-flows/ZoomPanHelper/index.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 | import ReactFlow, {
3 | ReactFlowProvider,
4 | addEdge,
5 | removeElements,
6 | } from 'react-flow-renderer';
7 |
8 | import Sidebar from './Sidebar';
9 |
10 | import './zoompanhelper.css';
11 |
12 | const initialElements = [
13 | {
14 | id: '1',
15 | type: 'input',
16 | data: { label: 'Node 1' },
17 | position: { x: 250, y: 5 },
18 | },
19 | { id: '2', data: { label: 'Node 2' }, position: { x: 100, y: 100 } },
20 | { id: '3', data: { label: 'Node 3' }, position: { x: 400, y: 100 } },
21 | { id: '4', data: { label: 'Node 4' }, position: { x: 400, y: 200 } },
22 | {
23 | id: 'e1-2',
24 | source: '1',
25 | target: '2',
26 | },
27 | { id: 'e1-3', source: '1', target: '3' },
28 | ];
29 |
30 | const ProviderFlow = () => {
31 | const [elements, setElements] = useState(initialElements);
32 | const onConnect = (params) => setElements((els) => addEdge(params, els));
33 | const onElementsRemove = (elementsToRemove) =>
34 | setElements((els) => removeElements(elementsToRemove, els));
35 |
36 | return (
37 |
38 |
39 |
40 |
45 |
46 |
47 |
48 |
49 | );
50 | };
51 |
52 | export default ProviderFlow;
53 |
--------------------------------------------------------------------------------
/src/example-flows/ZoomPanHelper/zoompanhelper.css:
--------------------------------------------------------------------------------
1 | .zoompanflow {
2 | flex-direction: column;
3 | display: flex;
4 | flex-grow: 1;
5 | }
6 |
7 | .zoompanflow aside {
8 | border-left: 1px solid #eee;
9 | padding: 15px 10px;
10 | font-size: 12px;
11 | background: #fff;
12 | }
13 |
14 | .zoompanflow aside .description {
15 | margin-bottom: 10px;
16 | }
17 |
18 | .zoompanflow aside button {
19 | display: block;
20 | margin-bottom: 5px;
21 | }
22 |
23 | .zoompanflow .reactflow-wrapper {
24 | flex-grow: 1;
25 | }
26 |
27 | @media screen and (min-width: 768px) {
28 | .zoompanflow {
29 | flex-direction: row;
30 | }
31 |
32 | .zoompanflow aside {
33 | width: 20%;
34 | max-width: 250px;
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/hooks/useExamplePages.js:
--------------------------------------------------------------------------------
1 | import { useMemo } from 'react';
2 | import { useStaticQuery, graphql } from 'gatsby';
3 |
4 | export default function useFeaturedProjects() {
5 | const rawData = useStaticQuery(graphql`
6 | query ExamplePages {
7 | allExamplesJson {
8 | edges {
9 | node {
10 | title
11 | slug
12 | }
13 | }
14 | }
15 | }
16 | `);
17 |
18 | const data = useMemo(() => {
19 | return rawData.allExamplesJson.edges.map(({ node }) => ({
20 | slug: `/${node.slug}`,
21 | title: node.title,
22 | }));
23 | }, [rawData]);
24 |
25 | return data;
26 | }
27 |
--------------------------------------------------------------------------------
/src/hooks/useMenuHeight.js:
--------------------------------------------------------------------------------
1 | import { useState, useEffect } from 'react';
2 |
3 | const getInnerHeight = () => {
4 | return typeof window !== 'undefined' ? window.innerHeight : 0;
5 | };
6 |
7 | export default function useMenuHeight() {
8 | const [menuHeight, setMenuHeight] = useState(getInnerHeight());
9 |
10 | useEffect(() => {
11 | const onResize = () => {
12 | setMenuHeight(getInnerHeight());
13 | };
14 |
15 | window.addEventListener('resize', onResize);
16 |
17 | return () => {
18 | window.removeEventListener('resize', onResize);
19 | };
20 | }, []);
21 |
22 | return menuHeight;
23 | }
24 |
--------------------------------------------------------------------------------
/src/hooks/useShowcaseImages.js:
--------------------------------------------------------------------------------
1 | import { useMemo } from 'react';
2 | import { useStaticQuery, graphql } from 'gatsby';
3 |
4 | export default function useFeaturedProjects() {
5 | const rawData = useStaticQuery(graphql`
6 | query ShowcaseImages {
7 | allShowcasesJson {
8 | edges {
9 | node {
10 | title
11 | url
12 | image {
13 | childImageSharp {
14 | gatsbyImageData(layout: CONSTRAINED, width: 600)
15 | }
16 | }
17 | }
18 | }
19 | }
20 | }
21 | `);
22 |
23 | const images = useMemo(
24 | () => rawData.allShowcasesJson.edges.map(({ node }) => node),
25 | [rawData]
26 | );
27 |
28 | return images;
29 | }
30 |
--------------------------------------------------------------------------------
/src/markdown/docs/api/components/background.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Background
3 | ---
4 |
5 | React Flow comes with two background variants: **dots** and **lines**. You can use it by passing it as a children to the `ReactFlow` component:
6 |
7 | ### Usage
8 |
9 | ```jsx
10 | import ReactFlow, { Background } from 'react-flow-renderer';
11 |
12 | const FlowWithBackground = () => (
13 |
14 |
19 |
20 | );
21 | ```
22 |
23 |
24 | ### Prop Types
25 |
26 | - `variant`: string - has to be 'dots' or 'lines' - default: `dots`
27 | - `gap`: number - the gap between the dots or lines - default: `16`
28 | - `size`: number - the radius of the dots or the stroke width of the lines - default: `0.5`
29 | - `color`: string - the color of the dots or lines - default: `#81818a` for dots, `#eee` for lines
30 | - `style`: css properties
31 | - `className`: additional class name
32 |
33 | **Typescript:** The interface of the Background Prop Types are exported as `BackgroundProps`.
--------------------------------------------------------------------------------
/src/markdown/docs/api/components/controls.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Controls
3 | ---
4 |
5 | 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:
6 |
7 | ### Usage
8 |
9 | ```jsx
10 | import ReactFlow, { Controls } from 'react-flow-renderer';
11 |
12 | const FlowWithControls = () => (
13 |
14 |
15 |
16 | );
17 | ```
18 |
19 | ### Prop Types
20 |
21 | - `showZoom`: boolean - default: true
22 | - `showFitView`: boolean - default: true
23 | - `showInteractive`: boolean - default: true
24 | - `style`: css properties
25 | - `className`: additional class name
26 | - `onZoomIn`: callback function that gets triggered when the zoom in button is pressed
27 | - `onZoomOut`: callback function that gets triggered when the zoom out button is pressed
28 | - `onFitView`: callback function that gets triggered when the fit-to-view button is pressed
29 | - `onInteractiveChange`: callback function that gets triggered when the lock button is pressed - passes the new value
30 |
31 | **Typescript:** The interface of the Controls Prop Types are exported as `ControlProps`.
32 |
33 | ## Extended Controls
34 |
35 | When you want to add buttons to the control panel you can use the `ControlsButton` component and pass it as a children to the `Controls` component:
36 |
37 | ```jsx
38 | import ReactFlow, { Controls, ControlButton } from 'react-flow-renderer';
39 |
40 | const FlowWithExtendedControls = () => (
41 |
42 |
43 | console.log('action')}>
44 |
45 |
46 | console.log('another action')}>
47 |
48 |
49 |
50 |
51 | );
52 | ```
53 |
54 | ### ControlsButton Prop Types
55 |
56 | All props get passed to the `ControlsButton` component.
57 |
--------------------------------------------------------------------------------
/src/markdown/docs/api/components/minimap.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Mini Map
3 | ---
4 |
5 | You can use the mini map plugin by passing it as a children to the `ReactFlow` component:
6 |
7 | ### Usage
8 |
9 | ```jsx
10 | import ReactFlow, { MiniMap } from 'react-flow-renderer';
11 |
12 | const FlowWithMiniMap = () => (
13 |
14 | {
16 | switch (node.type) {
17 | case 'input':
18 | return 'red';
19 | case 'default':
20 | return '#00ff00';
21 | case 'output':
22 | return 'rgb(0,0,255)';
23 | default:
24 | return '#eee';
25 | }
26 | }}
27 | nodeStrokeWidth={3}
28 | />
29 |
30 | );
31 | ```
32 |
33 | ### Prop Types
34 |
35 | - `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.
36 | - `nodeBorderRadius`: number
37 | - `nodeStrokeWidth`: number
38 | - `nodeClassName`: string or function for adding an additional class to the nodes inside the mini map
39 | - `maskColor`: string
40 | - `style`: css properties
41 | - `className`: additional class name
42 |
43 | **Typescript:** The interface of the MiniMap Prop Types are exported as `MiniMapProps`.
44 |
--------------------------------------------------------------------------------
/src/markdown/docs/api/components/provider.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Provider
3 | ---
4 |
5 | If you have **multiple flows** on a page or if you need **access to the internal state and actions** of React Flow outside of the `ReactFlow` component you need to wrap it with the `ReactFlowProvider` component:
6 |
7 | ```jsx
8 | import ReactFlow, { ReactFlowProvider } from 'react-flow-renderer';
9 |
10 | const FlowWithProvider = () => (
11 |
12 |
17 |
18 | );
19 | ```
20 |
21 | It is used in the [provider example](/examples/provider/).
22 |
23 | Note that if you are using the [bundle without styles](/docs/theming/), to correctly access internal state and actions you need to **import the provider and hooks from the same path**.
24 |
25 | ```js
26 | import ReactFlow from 'react-flow-renderer/nocss'
27 | export const Flow = () => (
28 |
33 | )
34 |
35 | // separate page
36 | import { ReactFlowProvider, useStoreState, useStoreActions } from 'react-flow-renderer/nocss';
37 | import Sidebar from './Sidebar';
38 |
39 | export const Wrapper = () => {
40 | const elements = useStoreState((state) => state.elements)
41 | const setElements = useStoreActions((actions) => actions.setElements);
42 | return (
43 |
44 |
45 |
46 |
47 | }
48 | ```
--------------------------------------------------------------------------------
/src/markdown/docs/api/edge-types.md:
--------------------------------------------------------------------------------
1 | ---
2 | id: Edge Types & Custom Edges
3 | title: Edge Types
4 | ---
5 |
6 | React Flow comes with four edge types (`default`, `straight`, `step`, `smoothstep`). As the names indicate, the edges differ in the representation. The default type is a bezier edge.
7 | The basic edge types are `default` (bezier), `straight`, `step` and `smoothstep`. The default `edgeTypes` object looks like this:
8 |
9 | ```javascript
10 | {
11 | default: BezierEdge,
12 | straight: StraightEdge,
13 | step: StepEdge,
14 | smoothstep: SmoothStepEdge
15 | }
16 | ```
17 |
18 | The keys represent the type names and the values are the edge components.
19 |
20 | # Custom Edges
21 |
22 | If you want to introduce a new edge type you can pass an `edgeTypes` object to the `ReactFlow` component:
23 |
24 | ```jsx
25 | const Flow = (
26 |
27 | );
28 | ```
29 |
30 | Now you could use the new type `special` for an edge.
31 | The `straight`, `default` and `step` types would still be available unless you overwrote one of them.
32 | There is an implementation of a custom edge in the [edges example](/examples/edges/).
33 |
34 | ## Prop Types
35 |
36 | Custom edges are wrapped. They receive the following props:
37 |
38 | - `source`: string (node id)
39 | - `target`: string (node id)
40 | - `selected`: boolean
41 | - `animated`: boolean
42 | - `label`: string
43 | - `labelStyle`: svg attributes
44 | - `labelShowBg`: boolean
45 | - `labelBgStyle`: svg attributes
46 | - `labelBgPadding`: number
47 | - `labelBgBorderRadius`: number
48 | - `data`: object
49 | - `style`: svg attributes
50 | - `arrowHeadType`: 'arrow' | 'arrowclosed'
51 | - `sourceX`: number
52 | - `sourceY`: number
53 | - `targetX`: number
54 | - `targetY`: number
55 | - `sourcePosition`: 'left' | 'top' | 'right' | 'bottom'
56 | - `targetPosition`: 'left' | 'top' | 'right' | 'bottom'
57 | - `markerEndId`: string
58 |
--------------------------------------------------------------------------------
/src/markdown/docs/api/edge-utils.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Edge Utils
3 | ---
4 |
5 | There are several utils that help you to create a custom edge. Some are used in the [edges example](/examples/edges).
6 |
7 | ### `getBezierPath`
8 |
9 | Returns the path of a bezier edge.
10 |
11 | ```
12 | import { getBezierPath } from 'react-flow-renderer';
13 |
14 | getBezierPath({
15 | sourceX,
16 | sourceY,
17 | sourcePosition = Position.Bottom, // optional
18 | targetX,
19 | targetY,
20 | targetPosition = Position.Top, // optional
21 | centerX, // optional
22 | centerY, // optional
23 | }: GetBezierPathParams): string
24 | ```
25 |
26 | ### `getSmoothStepPath`
27 |
28 | Returns the path of a smooth step edge. You can set `borderRadius` = `0` to get a step edge path.
29 |
30 | ```
31 | import { getSmoothStepPath } from 'react-flow-renderer';
32 |
33 | getSmoothStepPath({
34 | sourceX,
35 | sourceY,
36 | sourcePosition = Position.Bottom, // optional
37 | targetX,
38 | targetY,
39 | targetPosition = Position.Top, // optional
40 | borderRadius = 5, // optional
41 | centerX, // optional
42 | centerY, // optional
43 | }: GetSmoothStepPathParams): string
44 | ```
45 |
46 | ### `getEdgeCenter`
47 |
48 | Returns the center position and offset `[centerX, centerY, offsetX, offsetY]` of the edge.
49 |
50 | ```
51 | import { getEdgeCenter } from 'react-flow-renderer';
52 |
53 | getEdgeCenter({
54 | sourceX,
55 | sourceY,
56 | targetX,
57 | targetY
58 | }: GetCenterParams): [number, number, number, number]
59 | ```
60 |
61 | ### `getMarkerEnd`
62 |
63 | Returns the marker end url for displaying the arrow head.
64 |
65 | ```
66 | import { getMarkerEnd } from 'react-flow-renderer';
67 |
68 | getMarkerEnd(arrowHeadType?: ArrowHeadType, markerEndId?: string): string
69 | ```
70 |
71 | ### `EdgeText`
72 |
73 | The internal component that is used for displaying the edge label:
74 |
75 | ```js
76 | import { EdgeText } from 'react-flow-renderer';
77 |
78 | ;
88 | ```
89 |
90 | Besides the mentioned props you can pass all standard React HTML attributes like `onClick`, `className` and so on.
91 |
--------------------------------------------------------------------------------
/src/markdown/docs/api/edges.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Edge Options
3 | ---
4 |
5 | You create edges by adding them to your `elements` array of the `ReactFlow` component.
6 |
7 | Edge example:
8 |
9 | ```js
10 | {
11 | id: 'e1-2',
12 | type: 'straight',
13 | source: '1',
14 | target: '2',
15 | animated: true,
16 | label: 'edge label'
17 | }
18 | ```
19 |
20 | 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).
21 |
22 | ## Options
23 |
24 | - `id`: string *(required)*
25 | - `source`: string (an id of a node) *(required)*
26 | - `target`: string (an id of a node) *(required)*
27 | - `sourceHandle`: string (an id of a handle - you only need this when you have multiple handles)
28 | - `targetHandle`: string (an id of a handle - you only need this when you have multiple handles)
29 | - `type`: 'default' (bezier), 'straight', 'step' and 'smoothstep' or a custom one depending on your implementation
30 | - `animated`: boolean
31 | - `style`: css properties for the edge line path
32 | - `className`: additional class name
33 | - `label`: string
34 | - `labelStyle`: css properties for the text
35 | - `labelShowBg`: boolean - default: `true`
36 | - `labelBgStyle`: css properties for the text background
37 | - `labelBgPadding`: [number, number] background rectangle padding - default: `[2, 4]`
38 | - `labelBgBorderRadius`: number - default 2
39 | - `arrowHeadType`: 'arrow' or 'arrowclosed' - defines the arrowhead of the edge
40 | - `markerEndId`: custom marker end url - if this is used `arrowHeadType` gets ignored
41 | - `isHidden`: if `true`, the edge will not be rendered
42 | - `data`: {} you can use this to pass data to your custom edges.
43 |
44 | You can find an example with different edges in the [edges example](/examples/edges/).
45 |
--------------------------------------------------------------------------------
/src/markdown/docs/api/handle.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Handle Component
3 | ---
4 |
5 | We export a `Handle` component as a helper for your custom nodes:
6 |
7 | ```javascript
8 | import { Handle } from 'react-flow-renderer';
9 |
10 | const targetHandleWithValidation = (
11 | connection.source === 'some-id'}
15 | onConnect={(params) => console.log('handle onConnect', params)}
16 | style={{ background: '#fff' }}
17 | />
18 | );
19 | ```
20 |
21 | ### Prop Types
22 |
23 | - `type`: 'source' or 'target'
24 | - `id`: string - you only need this when you have multiple source or target handles (otherwise the node id is used)
25 | - `position`: 'left', 'right', 'top' or 'bottom' handle position - default: 'top' for type target, 'bottom' for type source
26 | - `onConnect`: function that gets triggered on connect. This callback only gets executed on source handles.
27 | - `isValidConnection`: function receives a connection `{ target: 'some-id', source: 'another-id', sourceHandle: 'source handle id or null', targetHandle: 'target handle id or null' }` as param, returns a boolean - default: `true`. This callback only gets executed on source handles.
28 | - `isConnectable` boolean (this prop gets passed to your custom node component)
29 | - `style`: css properties
30 | - `className`: additional class name
31 |
32 | **Typescript:** The interface of the Handle Prop Types are exported as `HandleProps`.
33 |
34 | ### Validation
35 |
36 | 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](/examples/validation/).
37 |
38 | ### Multiple Handles
39 |
40 | 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 can be used by an edge with the `sourceHandle` and `targetHandle` options, 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 node `source=1` and the `sourceHandle=a`.
41 |
42 | You can find an example of how to implement a custom node with multiple handles in the [custom node example](/examples/custom-node/).
43 |
44 | **Migrating Multiple Handles from v6 to v7+**
45 |
46 | The way multiple handles are treated has changed in v7.0.0.
47 |
48 | ```javascript
49 | // lets say we have 2 nodes, one input node with a source handle and a custom node with two target handles which have the ids a and b
50 | // our goal is to have a connection from the input node to each of the target handles of the custom node
51 | const nodes = [
52 | { id: '1', type: 'input', data: { label: 'Node with one source handle' } },
53 | {
54 | id: '2',
55 | type: 'multiHandleNode',
56 | data: { label: 'This node contains 2 handles with ids a and b' },
57 | },
58 | ];
59 |
60 | // in v6 and below we only had the target property to tell react-flow which handle is connected
61 | // this has been done using __ in the target id
62 | const elementsV6 = [
63 | ...nodes,
64 | {
65 | id: 'e1-2a',
66 | source: '1',
67 | target: '2__a', // targetNode 2, targetHandle a
68 | },
69 | {
70 | id: 'e1-2a',
71 | source: '1',
72 | target: '2__b', // targetNode 2, targetHandle b
73 | },
74 | ];
75 |
76 | // from v7 onwards we can use the targetHandle/sourceHandle properties to define which handle the edge is connecting exactly
77 | const elementsV7 = [
78 | { id: '1', type: 'input', data: { label: 'Node with one source handle' } },
79 | {
80 | id: '2',
81 | type: 'multiHandleNode',
82 | data: { label: 'This node contains 2 handles with ids a and b' },
83 | },
84 | {
85 | id: 'e1-2a',
86 | source: '1',
87 | target: '2',
88 | targetHandle: 'a',
89 | },
90 | {
91 | id: 'e1-2a',
92 | source: '1',
93 | target: '2',
94 | targetHandle: 'b',
95 | },
96 | ];
97 | ```
98 |
--------------------------------------------------------------------------------
/src/markdown/docs/api/helper-functions.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Helper Functions
3 | ---
4 |
5 | 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:
6 |
7 | ```javascript
8 | import ReactFlow, {
9 | isNode,
10 | isEdge,
11 | removeElements,
12 | addEdge,
13 | } from 'react-flow-renderer';
14 | ```
15 |
16 | ### `isEdge`
17 |
18 | Returns `true` if the passed element is an edge.
19 |
20 | `isEdge = (element: Node | Edge): element is Edge`
21 |
22 | ### `isNode`
23 |
24 | Returns `true` if the passed element is a node.
25 |
26 | `isNode = (element: Node | Edge): element is Node`
27 |
28 | ### `removeElements`
29 |
30 | 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.
31 |
32 | `removeElements = (elementsToRemove: Elements, elements: Elements): Elements`
33 |
34 | ### `addEdge`
35 |
36 | Returns an array with elements with the added edge.
37 |
38 | `addEdge = (edgeParams: Edge, elements: Elements): Elements`
39 |
40 | ### `updateEdge`
41 |
42 | Can be used as a helper for `onEdgeUpdate`. Returns the elements with the updated edge.
43 |
44 | `updateEdge = (oldEdge: Edge, newConnection: Connection, elements: Elements)`
45 |
46 | ### `getOutgoers`
47 |
48 | Returns all direct child nodes of the passed node.
49 |
50 | `getOutgoers = (node: Node, elements: Elements): Node[]`
51 |
52 | ### `getIncomers`
53 |
54 | Returns all direct incoming nodes of the passed node.
55 |
56 | `getIncomers = (node: Node, elements: Elements): Node[]`
57 |
58 | ### `getConnectedEdges`
59 |
60 | Returns all edges that are connected to the passed nodes.
61 |
62 | `getConnectedEdges = (nodes: Node[], edges: Edge[]): Edge[]`
63 |
64 | You can use these function as seen in [this example](https://github.com/wbkd/react-flow/blob/main/example/src/Overview/index.tsx#L100) or use your own ones.
65 |
--------------------------------------------------------------------------------
/src/markdown/docs/api/hooks.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Hooks
3 | ---
4 |
5 | The following hooks can only be used if the component that uses it is a children of a [`ReactFlowProvider`](/docs/api/components/provider/).
6 |
7 | ### useZoomPanHelper
8 |
9 | It can be used to modify the viewport of the react flow graph. Example:
10 |
11 | ```javascript
12 | import { useZoomPanHelper } from 'react-flow-renderer';
13 |
14 | export default () => {
15 | const { fitView } = useZoomPanHelper();
16 |
17 | return (
18 | fitView({ padding: 0.2, includeHiddenNodes: true })}
20 | >
21 | );
22 | };
23 | ```
24 |
25 | The `useZoomPanHelper` hook returns an object containing the following functions:
26 |
27 | - `fitView = ({ padding: number, includeHiddenNodes: boolean }): void` - fits the view to the nodes on the pane. `padding` is `0.1` and `includeHiddenNodes` is `false` by default
28 | - `zoomIn = (): void` - zooms in
29 | - `zoomOut = (): void` - zooms out
30 | - `zoomTo = (zoomLevel: number): void` - zooms to passed zoom level
31 | - `transform: (transform: FlowTransform): void` - sets the transform of the pane
32 | - `setCenter: (x: number, y: number, zoom?: number): void` - sets the center to the passed params. If no zoom is passed the maxZoom is used
33 | - `fitBounds: (bounds: Rect, padding?: number): void` - fits the pane to the passed bounds (object with width x, y, width and height: `{ x: 0, y: 0, width: 100, height: 100 }`)
34 | - `project: (position: XYPosition) => XYPosition`: 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 internal position on the pane.
35 | - `initialized: boolean` - `true` when hook is initialized
36 |
37 | You can find an example of how to use it here: [useZoomPanHelper example](/examples/use-zoom-pan-helper-hook/)
38 |
39 | ### useUpdateNodeInternals
40 |
41 | When you are **programatically changing the number or the position of handles inside a custom node** you need to notify react flow about it with the `useUpdateNodeInternals` hook. It also updates the internal size. Usage:
42 |
43 | ```javascript
44 | import { useUpdateNodeInternals } from 'react-flow-renderer';
45 |
46 | export default () => {
47 | const updateNodeInternals = useUpdateNodeInternals();
48 |
49 | return updateNodeInternals('node-id')}> ;
50 | };
51 | ```
52 |
--------------------------------------------------------------------------------
/src/markdown/docs/api/internal-state-actions.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Internal State and Actions
3 | ---
4 |
5 | Under the hood React Flow uses [Redux](https://redux.js.org/) for state handling.
6 | If you need to access the internal state you can use the `useStoreState` hook inside a child component of the `ReactFlow` component:
7 |
8 | ### Internal state
9 |
10 | ```jsx
11 | import ReactFlow, { useStoreState } from 'react-flow-renderer';
12 |
13 | const NodesDebugger = () => {
14 | const nodes = useStoreState((state) => state.nodes);
15 |
16 | console.log(nodes);
17 |
18 | return null;
19 | };
20 |
21 | const Flow = () => (
22 |
23 |
24 |
25 | );
26 | ```
27 |
28 | ### Internal actions
29 |
30 |
31 |
32 | You will not need this in most cases but you can also use the internal actions that are defined in the [store](https://github.com/wbkd/react-flow/blob/main/src/store/index.ts):
33 |
34 | ```jsx
35 | import React, { useEffect } from 'react';
36 | import { useStoreActions } from 'react-flow-renderer'
37 |
38 | const MinZoom = () => {
39 | const setMinZoom = useStoreActions(actions => actions.setMinZoom);
40 |
41 | useEffect(() => {
42 | setMinZoom(6);
43 | }, []);
44 |
45 | return null;
46 | });
47 | ```
48 |
49 | If you need more control you can wrap the `ReactFlow` component with the [`ReactFlowProvider`](/docs/api/components/provider/) component in order to be able to call `useStoreState` and `useStoreActions` outside of the `ReactFlow` component.
50 |
--------------------------------------------------------------------------------
/src/markdown/docs/api/node-types/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | id: Node Types & Custom Nodes
3 | title: Node Types
4 | ---
5 |
6 | There are three different node types (`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. The default node types object looks like this:
7 |
8 | ```js
9 | {
10 | input: InputNode,
11 | default: DefaultNode,
12 | output: OutputNode
13 | }
14 | ```
15 |
16 | The keys represent the type names and the values are the components that get rendered.
17 |
18 | # Custom Nodes
19 |
20 | If you want to introduce new node types you need to pass a `nodeTypes` object to the `ReactFlow` component:
21 |
22 | ```jsx
23 | const Flow = (
24 |
25 | );
26 | ```
27 |
28 | You can now use the type `special` for a node.
29 | The `default`, `input` and `output` types would still be available except you overwrote one of them.
30 | There is more advanced example of a custom node implementation in the [custom node example](/examples/custom-node).
31 |
32 | ## Prop Types
33 |
34 | Your custom nodes are wrapped so that the basic functions like dragging or selecting work. Custom nodes receive the following props:
35 |
36 | - `id`: string
37 | - `data`: object
38 | - `type`: string
39 | - `selected`: boolean
40 | - `sourcePosition`: string
41 | - `targetPosition`: string
42 |
43 | ### Styling
44 |
45 | When you create a new node type you also need to implement some styling. Your custom node has no default styles.
46 |
47 | ### Prevent dragging & selecting
48 |
49 | If you have controls or other elements inside your custom node that should not drag the node you can add the class name `nodrag`. This also prevents the selection of a node.
50 |
51 | ### Allow scrolling inside a node
52 |
53 | If you want to allow scrolling inside a node or inside an element of a node you can add the class name `nowheel` to the node or the element.
54 |
55 | ## Basic Implementation
56 |
57 | A basic implementation of a custom node could look like this:
58 |
59 | ```jsx
60 | import React from 'react';
61 | import ReactFlow, { Handle, Position } from 'react-flow-renderer';
62 |
63 | const elements = [
64 | {
65 | id: '2',
66 | type: 'special',
67 | position: { x: 100, y: 100 },
68 | data: { text: 'A custom node' },
69 | },
70 | ];
71 |
72 | const customNodeStyles = {
73 | background: '#9CA8B3',
74 | color: '#FFF',
75 | padding: 10,
76 | };
77 |
78 | const CustomNodeComponent = ({ data }) => {
79 | return (
80 |
81 |
82 |
{data.text}
83 |
89 |
95 |
96 | );
97 | };
98 |
99 | const nodeTypes = {
100 | special: CustomNodeComponent,
101 | };
102 |
103 | const CustomNodeExample = () => {
104 | return (
105 |
106 |
107 |
108 | );
109 | };
110 |
111 | export default CustomNodeExample;
112 | ```
113 |
114 | import CustomNodeFlow from './node-types';
115 |
116 |
117 |
--------------------------------------------------------------------------------
/src/markdown/docs/api/node-types/node-types.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactFlow, { Handle, ReactFlowProvider } from 'react-flow-renderer';
3 |
4 | const elementsCustom = [
5 | {
6 | id: '2',
7 | type: 'special',
8 | position: { x: 100, y: 100 },
9 | data: { text: 'A custom node' },
10 | },
11 | ];
12 |
13 | const customNodeStyles = {
14 | background: '#9CA8B3',
15 | color: '#FFF',
16 | padding: 10,
17 | };
18 |
19 | const CustomNodeComponent = ({ data }) => {
20 | return (
21 |
22 |
23 |
{data.text}
24 |
30 |
36 |
37 | );
38 | };
39 |
40 | const nodeTypes = {
41 | special: CustomNodeComponent,
42 | };
43 |
44 | const wrapperStyle = { height: 300, border: '1px solid #C5CBD2' };
45 |
46 | const CustomNodeExample = () => {
47 | return (
48 |
49 |
50 |
51 |
52 |
53 | );
54 | };
55 |
56 | export default CustomNodeExample;
57 |
--------------------------------------------------------------------------------
/src/markdown/docs/api/nodes.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Node Options
3 | ---
4 |
5 | You create nodes by adding them to the `elements` array of the `ReactFlow` component.
6 |
7 | Node example:
8 |
9 | ```js
10 | {
11 | id: '1',
12 | type: 'input',
13 | data: { label: 'Node 1' },
14 | position: { x: 250, y: 5 }
15 | }
16 | ```
17 |
18 | ## Options
19 |
20 | - `id`: string _(required)_
21 | - `position`: { x: number, y: number } _(required)_
22 | - `data`: {} _(required if you are using a standard type, otherwise depends on your implementation)_
23 | - `type`: 'input' | 'output' | 'default' or a custom one you implemented
24 | - `style`: css properties
25 | - `className`: additional class name
26 | - `targetPosition`: 'left' | 'right' | 'top' | 'bottom' handle position - default: 'top'
27 | - `sourcePosition`: 'left' | 'right' | 'top' | 'bottom' handle position - default: 'bottom'
28 | - `isHidden`: if `true`, the node will not be rendered
29 | - `draggable`: boolean - if option is not set, the node is draggable (overwrites general `nodesDraggable` option)
30 | - `connectable`: boolean - if option is not set, the node is connectable (overwrites general `nodesConnectable` option)
31 | - `selectable`: boolean - if option is not set, the node is selectable (overwrites general `elementsSelectable` option)
32 | - `dragHandle`: string - selector for specifying an element as a drag handle
33 |
34 | ## Update Node Options
35 |
36 | If you want to update the `data` or `style` attribute of a node it is important that you create a new object to notify react flow about the changes. You can see how to update a node in the [update node example](/examples/update-node/).
37 |
--------------------------------------------------------------------------------
/src/markdown/docs/api/react-flow-instance.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Instance
3 | ---
4 |
5 | You can receive a `reactFlowInstance` by using the `onLoad` callback:
6 |
7 | ```javascript
8 | import React from 'react';
9 | import ReactFlow from 'react-flow-renderer';
10 |
11 | const onLoad = (reactFlowInstance) => {
12 | reactFlowInstance.fitView();
13 | };
14 |
15 | const BasicFlow = () => ;
16 | ```
17 |
18 | The `reactFlowInstance` has the following functions:
19 |
20 | ### `project`
21 |
22 | Transforms pixel coordinates to the internal ReactFlow coordinate system.
23 | This can be used when you drag nodes (from a side bar for example) and need the internal position on the pane.
24 |
25 | `project = (position: XYPosition): XYPosition`
26 |
27 | **example:**
28 |
29 | ```
30 | reactFlowInstance.project({ x: 100, y: 100 });
31 | ```
32 |
33 | ### `fitView`
34 |
35 | Fits the view port so that all nodes are visible. `padding` is `0.1` and `includeHiddenNodes` is `false` by default.
36 |
37 | `fitView = ({ padding, includeHiddenNodes }): void`
38 |
39 | **example:**
40 |
41 | ```
42 | reactFlowInstance.fitView({ padding: 0.25, includeHiddenNodes: true });
43 | ```
44 |
45 | ### `zoomIn`
46 |
47 | Zoom in
48 |
49 | `zoomIn = (): void`
50 |
51 | ### `zoomOut`
52 |
53 | Zoom out
54 |
55 | `zoomOut = (): void`
56 |
57 | ### `zoomTo`
58 |
59 | Zooms to the specified zoom level
60 |
61 | `zoomTo = (zoomLevel: number): void`
62 |
63 | ### `setTransform`
64 |
65 | Sets position and zoom of the pane.
66 |
67 | `setTransform = (transform: FlowTransform): void`
68 |
69 | **example:**
70 |
71 | ```
72 | reactFlowInstance.setTransform({ x: 100, y: 100, zoom: 1.5 });
73 | ```
74 |
75 | ### `toObject`
76 |
77 | Returns `elements`, `position` and `zoom` of the current flow state
78 |
79 | ```
80 | toObject = (): {
81 | elements: Elements,
82 | position: [x, y],
83 | zoom: scale,
84 | }
85 | ```
86 |
87 | ### `getElements`
88 |
89 | `getElements = (): Elements`
90 |
--------------------------------------------------------------------------------
/src/markdown/docs/getting-started/Basic.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactFlow, { ReactFlowProvider } from 'react-flow-renderer';
3 |
4 | const elements = [
5 | // input node
6 | {
7 | id: '1',
8 | type: 'input',
9 | data: { label: 'Input Node' },
10 | position: { x: 250, y: 25 },
11 | },
12 | // default node
13 | {
14 | id: '2',
15 | // you can also pass a React component as a label
16 | data: { label: Default Node
},
17 | position: { x: 100, y: 125 },
18 | },
19 | // output node
20 | {
21 | id: '3',
22 | type: 'output',
23 | // you can also pass a React component as a label
24 | data: { label: 'Output Node' },
25 | position: { x: 250, y: 250 },
26 | },
27 | { id: 'e1-2', source: '1', target: '2', animated: true },
28 | { id: 'e2-3', source: '2', target: '3' },
29 | ];
30 |
31 | export default () => (
32 |
33 |
34 |
35 |
36 |
37 | );
38 |
--------------------------------------------------------------------------------
/src/markdown/docs/getting-started/BasicFunctions.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 | import ReactFlow, {
3 | removeElements,
4 | addEdge,
5 | ReactFlowProvider,
6 | } from 'react-flow-renderer';
7 |
8 | const initialElements = [
9 | {
10 | id: '1',
11 | type: 'input',
12 | data: { label: 'Input Node' },
13 | position: { x: 250, y: 25 },
14 | },
15 | {
16 | id: '2',
17 | data: { label: 'Another Node' },
18 | position: { x: 100, y: 125 },
19 | },
20 | ];
21 |
22 | const BasicFlow = () => {
23 | const [elements, setElements] = useState(initialElements);
24 | const onElementsRemove = (elementsToRemove) =>
25 | setElements((els) => removeElements(elementsToRemove, els));
26 | const onConnect = (params) => setElements((els) => addEdge(params, els));
27 |
28 | return (
29 |
30 |
31 |
36 |
37 |
38 | );
39 | };
40 |
41 | export default BasicFlow;
42 |
--------------------------------------------------------------------------------
/src/markdown/docs/getting-started/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Getting Started
3 | ---
4 |
5 | Before you can start to use React Flow you need to install `react-flow-renderer`:
6 |
7 | ## Installation
8 |
9 | **npm:**
10 |
11 | ```bash
12 | npm install react-flow-renderer
13 | ```
14 |
15 | **yarn:**
16 |
17 | ```bash
18 | yarn add react-flow-renderer
19 | ```
20 |
21 | ## Usage
22 |
23 | This is a very basic example of how to use React Flow. A flow consists of nodes and edges (or just nodes). Together we call them elements. You can pass a set of elements as a prop to the ReactFlow component. Hereby all elements need unique ids. A node needs a position and a label and an edge needs a source (node id) and a target (node id). This is the most basic for a flow. A simple flow could look like this:
24 |
25 | ```jsx
26 | import React from 'react';
27 | import ReactFlow from 'react-flow-renderer';
28 |
29 | const elements = [
30 | {
31 | id: '1',
32 | type: 'input', // input node
33 | data: { label: 'Input Node' },
34 | position: { x: 250, y: 25 },
35 | },
36 | // default node
37 | {
38 | id: '2',
39 | // you can also pass a React component as a label
40 | data: { label: Default Node
},
41 | position: { x: 100, y: 125 },
42 | },
43 | {
44 | id: '3',
45 | type: 'output', // output node
46 | data: { label: 'Output Node' },
47 | position: { x: 250, y: 250 },
48 | },
49 | // animated edge
50 | { id: 'e1-2', source: '1', target: '2', animated: true },
51 | { id: 'e2-3', source: '2', target: '3' },
52 | ];
53 |
54 | export default () => (
55 |
56 |
57 |
58 | );
59 | ```
60 |
61 | import Flow from './Basic';
62 |
63 |
64 |
65 |
66 |
67 | ## Basic Functionality
68 |
69 | 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](/docs/api/helper-functions/) that come with the library. Here you see an example of how to use the helper functions `removeElements` and `addEdge`.
70 |
71 | ```jsx
72 | import React, { useState } from 'react';
73 | import ReactFlow, { removeElements, addEdge } from 'react-flow-renderer';
74 |
75 | const initialElements = [
76 | {
77 | id: '1',
78 | type: 'input',
79 | data: { label: 'Input Node' },
80 | position: { x: 250, y: 25 },
81 | },
82 | {
83 | id: '2',
84 | data: { label: 'Another Node' },
85 | position: { x: 100, y: 125 },
86 | },
87 | ];
88 |
89 | export default () => {
90 | const [elements, setElements] = useState(initialElements);
91 | const onElementsRemove = (elementsToRemove) =>
92 | setElements((els) => removeElements(elementsToRemove, els));
93 | const onConnect = (params) => setElements((els) => addEdge(params, els));
94 |
95 | return (
96 |
97 |
103 |
104 | );
105 | };
106 | ```
107 |
108 | In this example you can connect nodes and remove selected nodes and edges with the backspace key.
109 |
110 | import Basic from './BasicFunctions';
111 |
112 |
113 |
114 | You can find more advanced examples in the [examples](/examples/) section.
115 |
--------------------------------------------------------------------------------
/src/markdown/docs/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Introduction
3 | ---
4 |
5 | React Flow is a library for building node-based applications. These can be simple static diagrams or complex node-based editors. You can implement custom node types and edge types and it comes with components like a mini-map and graph controls. Feel free to check out the [examples](https://reactflow.dev/examples/) or read the [blog post](https://webkid.io/blog/react-flow-node-based-graph-library/) to get started.
6 |
7 | ## Key Features
8 |
9 | * **Easy to use:** Seamless zooming & panning behaviour and single and multi-selections of elements
10 | * **Customizable:** Different [node](/docs/api/node-types/) and [edge types](/docs/api/edge-types) and support for custom nodes with multiple handles and custom edges
11 | * **Fast rendering:** Only nodes that have changed are re-rendered and only those that are in the viewport are displayed
12 | * **Utils:** Snap-to-grid and graph [helper functions](/docs/api/helper-functions/)
13 | * **Components:** [Background](/docs/api/components/background/), [Minimap](/docs/api/components/minimap/) and [Controls](/docs/api/components/controls/)
14 | * **Reliable**: Written in [Typescript](https://www.typescriptlang.org/) and tested with [cypress](https://www.cypress.io/)
15 |
--------------------------------------------------------------------------------
/src/markdown/docs/theming.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Theming
3 | ---
4 |
5 | React Flow offers two bundles. One comes with styles included and the other without.
6 |
7 | ### Load bundle with styles
8 |
9 | ```js
10 | import ReactFlow from 'react-flow-renderer';
11 |
12 | // no need to import styles
13 | ```
14 |
15 | ### Load bundle without styles
16 |
17 | ```js
18 | import ReactFlow from 'react-flow-renderer/nocss';
19 |
20 | // you need these styles for React Flow to work properly
21 | import 'react-flow-renderer/dist/style.css';
22 |
23 | // additionally you can load the default theme
24 | import 'react-flow-renderer/dist/theme-default.css';
25 | ```
26 |
27 | ## Overwrite Default Styles
28 |
29 | When you are using the default styles there are two ways how you can style the graph pane and the elements.
30 | You can create your own CSS rules or pass style properties to the components.
31 |
32 | ## Using Class Names
33 |
34 | Since we are rendering DOM nodes you can simply overwrite the styles with your own CSS rules.
35 | The React Flow wrapper has the className `react-flow`. If you want to change the graph background for example you can do:
36 |
37 | ```css
38 | .react-flow {
39 | background: red;
40 | }
41 | ```
42 |
43 | ### React Flow Class Names
44 |
45 | - `.react-flow` - Outer container
46 | - `.react-flow__renderer` - Inner container
47 | - `.react-flow__zoompane` - Zoom & pan pane
48 | - `.react-flow__selectionpane` - Selection pane
49 | - `.react-flow__selection` - User selection
50 | - `.react-flow__edges` - Edges wrapper
51 | - `.react-flow__edge` - Edge element
52 | - `.selected` is added when edge is selected
53 | - `.animated` is added when edge is animated
54 | - `.react-flow__edge-path` - Edge element path
55 | - `.react-flow__edge-text` - Edge text
56 | - `.react-flow__edge-textbg` - Edge text background
57 | - `.react-flow__connection` - Connection line
58 | - `.react-flow__connection-path` - Connection line path
59 | - `.react-flow__nodes` - Nodes wrapper
60 | - `.react-flow__node` - Node element
61 | - `.selected` is added when edge is selected
62 | - `-${type}` is added (`.react-flow__node-default`, `.react-flow__node-input`, `.react-flow__node-output`)
63 | - `.react-flow__nodesselection` - Nodes selection
64 | - `.react-flow__nodesselection-rect ` - Nodes selection rect
65 | - `.react-flow__handle` - Handle component
66 | - `.react-flow__handle-bottom` is added when position = 'bottom'
67 | - `.react-flow__handle-top` is added when position = 'top'
68 | - `.react-flow__handle-left` is added when position = 'left'
69 | - `.react-flow__handle-right` is added when position = 'right'
70 | - `.react-flow__handle-connecting` is added when connection line is above a handle
71 | - `.react-flow__handle-valid` is added when connection line is above a handle and the connection is valid
72 | - `.react-flow__background` - Background component
73 | - `.react-flow__minimap` - Mini map component
74 | - `.react-flow__controls` - Controls component
75 |
76 | ## Using Properties
77 |
78 | You could achieve the same effect by passing a style prop to the `ReactFlow` component:
79 |
80 | ```jsx
81 | const style = {
82 | background: 'red',
83 | width: '100%',
84 | height: 300,
85 | };
86 |
87 | const FlowWithRedBg = ;
88 | ```
89 |
--------------------------------------------------------------------------------
/src/pages/404.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import Page from 'components/Page';
4 |
5 | const metaTags = {
6 | title: 'React Flow - 404',
7 | siteUrl: 'https://reactflow.dev/404',
8 | robots: 'noindex, nofollow',
9 | };
10 |
11 | const NotFound = () => {
12 | return Page not Found ;
13 | };
14 |
15 | export default NotFound;
16 |
--------------------------------------------------------------------------------
/src/styles/global.css:
--------------------------------------------------------------------------------
1 | @font-face {
2 | font-family: 'nt-dapper';
3 | src: url('https://webkid.io/fonts/NTDapper-regular.woff2') format('woff2'),
4 | url('https://webkid.io/fonts/NTDapper-regular.woff') format('woff'),
5 | url('https://webkid.io/fonts/NTDapper-regular.eot');
6 | font-weight: 400;
7 | font-style: normal;
8 | }
9 |
10 | @font-face {
11 | font-family: 'nt-dapper';
12 | src: url('https://webkid.io/fonts/NTDapper-bold.woff2') format('woff2'),
13 | url('https://webkid.io/fonts/NTDapper-bold.woff') format('woff'),
14 | url('https://webkid.io/fonts/NTDapper-bold.eot');
15 | font-weight: 700;
16 | font-style: normal;
17 | }
18 |
19 | @font-face {
20 | font-family: 'nt-dapper';
21 | src: url('https://webkid.io/fonts/NTDapper-black.woff2') format('woff2'),
22 | url('https://webkid.io/fonts/NTDapper-black.woff') format('woff'),
23 | url('https://webkid.io/fonts/NTDapper-black.eot');
24 | font-weight: 900;
25 | font-style: normal;
26 | }
27 |
28 | @font-face {
29 | font-family: 'jetbrains-mono';
30 | src: url('../../static/fonts/JetBrainsMono-Regular.woff2') format('woff2'),
31 | url('../../static/fonts/JetBrainsMono-Regular.woff') format('woff'),
32 | url('../../static/fonts/JetBrainsMono-Regular.eot');
33 | font-weight: 400;
34 | font-style: normal;
35 | }
36 |
37 | .noscroll {
38 | height: 100vh;
39 | overflow: hidden;
40 | position: fixed;
41 | width: 100%;
42 | }
43 |
44 | .react-flow {
45 | font-family: 'jetbrains-mono';
46 | text-transform: uppercase;
47 | }
48 |
49 | .react-flow .react-flow__node {
50 | font-size: 14px;
51 | }
52 |
53 | .react-flow__controls-button {
54 | width: 16px;
55 | height: 16px;
56 | }
57 |
--------------------------------------------------------------------------------
/src/templates/doc-page.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { graphql } from 'gatsby';
3 | import { Flex, Box } from 'reflexbox';
4 | import styled from '@emotion/styled';
5 |
6 | import DocPage from 'components/Page/Doc';
7 | import Mdx from './mdx-renderer/DocMdx';
8 | import { H1 } from 'components/Typo';
9 | import Icon from 'components/Icon';
10 |
11 | const docsMenu = [
12 | { title: 'Introduction' },
13 | { title: 'Getting Started' },
14 | { title: 'Theming' },
15 | {
16 | group: 'API Reference',
17 | items: [
18 | { title: 'Prop Types' },
19 | { title: 'Helper Functions' },
20 | {
21 | group: 'Nodes',
22 | items: [
23 | { title: 'Node Options' },
24 | { title: 'Node Types & Custom Nodes' },
25 | { title: 'Handle Component' },
26 | ],
27 | },
28 | {
29 | group: 'Edges',
30 | items: [
31 | { title: 'Edge Options' },
32 | { title: 'Edge Types & Custom Edges' },
33 | { title: 'Edge Utils' },
34 | ],
35 | },
36 | { title: 'Instance' },
37 | { title: 'Internal State and Actions' },
38 | { title: 'Hooks' },
39 | {
40 | group: 'Components',
41 | items: [
42 | { title: 'Background' },
43 | { title: 'Mini Map' },
44 | { title: 'Controls' },
45 | { title: 'Provider' },
46 | ],
47 | },
48 | ],
49 | },
50 | ];
51 |
52 | const EditLink = styled.a`
53 | display: flex;
54 | align-items: center;
55 |
56 | &:hover {
57 | opacity: 0.6;
58 | }
59 |
60 | .icon {
61 | margin-right: 5px;
62 | }
63 | `;
64 |
65 | const EditButton = ({ slug }) => (
66 |
67 |
72 |
73 | Edit this page
74 |
75 |
76 |
77 | current version: {__REACT_FLOW_VERSION__}
78 |
79 |
80 | );
81 |
82 | function extendMenu(items, menuData) {
83 | items.forEach((menuItem) => {
84 | if (menuItem.group) {
85 | return extendMenu(menuItem.items, menuData);
86 | }
87 |
88 | menuItem.slug =
89 | menuData.find((m) => (m.id || m.title) === menuItem.title)?.slug || '/';
90 | });
91 | }
92 |
93 | const DocPageTemplate = ({ data, pageContext }) => {
94 | const { content } = data;
95 | const { title } = content.frontmatter;
96 | const slug = content.fileAbsolutePath.split('markdown')[1];
97 | const metaTags = {
98 | title: `React Flow - ${title} Docs`,
99 | description: content.excerpt,
100 | siteUrl: `https://reactflow.dev${content.fields.slug}`,
101 | robots: 'index, follow',
102 | };
103 |
104 | extendMenu(docsMenu, pageContext.menu);
105 |
106 | return (
107 |
108 | {title}
109 |
110 |
111 |
112 | );
113 | };
114 |
115 | export default DocPageTemplate;
116 |
117 | export const pageQuery = graphql`
118 | query DocPageBySlug($slug: String!) {
119 | content: mdx(fields: { slug: { eq: $slug } }) {
120 | id
121 | fileAbsolutePath
122 | excerpt
123 | body
124 | fields {
125 | slug
126 | }
127 | frontmatter {
128 | id
129 | title
130 | }
131 | }
132 | }
133 | `;
134 |
--------------------------------------------------------------------------------
/src/templates/example-page.js:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from 'react';
2 | import { graphql } from 'gatsby';
3 | import { ReactFlowProvider } from 'react-flow-renderer';
4 |
5 | import ExamplePage from 'components/Page/Example';
6 |
7 | export default ({ data, pageContext }) => {
8 | const [flow, setFlow] = useState(null);
9 | const sourceCodeFiles = data?.allFile?.edges
10 | ?.map(({ node }) => node)
11 | .sort((a, b) => {
12 | if (a.absolutePath.includes('index.js')) {
13 | return -1;
14 | }
15 | })
16 | .filter((node) => node.internal.content);
17 |
18 | useEffect(() => {
19 | const load = async () => {
20 | const nextFlow = await import(`example-flows/${pageContext.source}`);
21 | setFlow(nextFlow);
22 | };
23 |
24 | load();
25 | }, [pageContext]);
26 |
27 | return (
28 |
34 | {flow && }
35 |
36 | );
37 | };
38 |
39 | export const pageQuery = graphql`
40 | query SourceBySourceSlug($sourceSlug: String!) {
41 | allFile(filter: { absolutePath: { regex: $sourceSlug } }) {
42 | edges {
43 | node {
44 | id
45 | absolutePath
46 | internal {
47 | content
48 | }
49 | }
50 | }
51 | }
52 | }
53 | `;
54 |
--------------------------------------------------------------------------------
/src/templates/mdx-renderer/DefaultMdx.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { MDXProvider } from '@mdx-js/react';
3 | import { MDXRenderer } from 'gatsby-plugin-mdx';
4 |
5 | const Mdx = ({ content = null }) => {
6 | return (
7 |
8 | {content}
9 |
10 | );
11 | };
12 |
13 | export default Mdx;
14 |
--------------------------------------------------------------------------------
/src/templates/mdx-renderer/DocMdx.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import styled from '@emotion/styled';
3 | import slugify from 'slugify';
4 | import { MDXProvider } from '@mdx-js/react';
5 | import { MDXRenderer } from 'gatsby-plugin-mdx';
6 |
7 | import { H1, H2, H3, H4, Text } from 'components/Typo';
8 | import CodeBlock from 'components/CodeBlock/Mdx';
9 | import InfoBox from 'components/InfoBox';
10 | import Link from 'components/Link';
11 | import { getThemeSpacePx } from 'utils/css-utils';
12 |
13 | const DocWrapper = styled.div`
14 | ${Text} {
15 | margin-bottom: ${getThemeSpacePx(3)};
16 | margin-top: ${getThemeSpacePx(3)};
17 |
18 | code {
19 | background: rgb(246, 248, 250);
20 | border-radius: 2px;
21 | padding: 0 3px;
22 | font-size: 14px;
23 | }
24 | }
25 |
26 | ul,
27 | ol {
28 | padding-left: ${getThemeSpacePx(4)};
29 | }
30 |
31 | li {
32 | margin-bottom: ${getThemeSpacePx(2)};
33 |
34 | code {
35 | font-size: 14px;
36 | }
37 | }
38 |
39 | hr {
40 | max-width: calc(680px - 32px);
41 | }
42 |
43 | h1 {
44 | margin-top: ${getThemeSpacePx(6)};
45 | }
46 |
47 | h2 {
48 | margin-top: ${getThemeSpacePx(5)};
49 | }
50 |
51 | h3 {
52 | margin-top: ${getThemeSpacePx(4)};
53 | }
54 |
55 | h1,
56 | h2,
57 | h3,
58 | h4,
59 | h5 {
60 | a {
61 | color: ${(p) => p.theme.colors.text};
62 | }
63 | }
64 |
65 | iframe {
66 | display: block;
67 | width: 100%;
68 | }
69 |
70 | .gatsby-resp-image-wrapper {
71 | border: 1px solid ${(p) => p.theme.colors.silver};
72 | border-radius: 2px;
73 | overflow: hidden;
74 | margin-bottom: ${getThemeSpacePx(5)};
75 | margin-top: ${getThemeSpacePx(5)};
76 | }
77 |
78 | em {
79 | .gatsby-resp-image-figure {
80 | margin: 0;
81 | }
82 |
83 | .gatsby-resp-image-figcaption {
84 | font-style: normal;
85 | }
86 | }
87 |
88 | .gatsby-resp-image-figure {
89 | max-width: 1200px;
90 | }
91 |
92 | .gatsby-resp-image-figcaption {
93 | color: ${(p) => p.theme.colors.textLight};
94 | margin-top: -${getThemeSpacePx(5)};
95 | text-align: center;
96 | }
97 |
98 | a:hover {
99 | opacity: 0.6;
100 | }
101 | `;
102 |
103 | const DocH1 = styled(H1)`
104 | margin: ${getThemeSpacePx(6)} 0 ${getThemeSpacePx(3)} 0;
105 | `;
106 |
107 | const DocH2 = styled(H2)`
108 | margin: ${getThemeSpacePx(6)} 0 ${getThemeSpacePx(3)} 0;
109 | `;
110 |
111 | const DocH3 = styled(H3)`
112 | margin: ${getThemeSpacePx(6)} 0 ${getThemeSpacePx(3)} 0;
113 | `;
114 |
115 | const DocH4 = styled(H4)`
116 | margin: ${getThemeSpacePx(6)} 0 ${getThemeSpacePx(3)} 0;
117 | `;
118 |
119 | export const getAnchorId = (props = {}) => {
120 | let id = '';
121 | let nextChildren = props.children;
122 |
123 | // when a react component is passed instead of just text
124 | // we need to go through the children until we reach the text content
125 | while (nextChildren) {
126 | if (typeof nextChildren === 'string') {
127 | id = slugify(nextChildren, { lower: true });
128 | }
129 |
130 | nextChildren = nextChildren?.props?.children;
131 | }
132 |
133 | return id;
134 | };
135 |
136 | const CustomComponents = {
137 | h1: (props) => ,
138 | h2: (props) => ,
139 | h3: (props) => ,
140 | h4: (props) => ,
141 | p: Text,
142 | pre: (props) =>
,
143 | code: CodeBlock,
144 | a: Link,
145 | InfoBox,
146 | };
147 |
148 | const BlogMdx = ({ content = null }) => {
149 | return (
150 |
151 |
152 | {content}
153 |
154 |
155 | );
156 | };
157 |
158 | export default BlogMdx;
159 |
--------------------------------------------------------------------------------
/src/themes/global.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { useTheme, Global, css, jsx } from '@emotion/react';
3 |
4 | const GlobalStyle = () => {
5 | const theme = useTheme();
6 |
7 | const globalStyles = css`
8 | html,
9 | body {
10 | background-color: ${theme.colors.background};
11 | font-family: ${theme.fonts.sans};
12 | font-weight: 400;
13 | letter-spacing: 0.5px;
14 | line-height: 1.5;
15 | font-size: 16px;
16 | padding: 0;
17 | margin: 0;
18 | min-height: 100vh;
19 | }
20 |
21 | a {
22 | color: ${theme.colors.red};
23 | text-decoration: none;
24 | }
25 |
26 | a:visited,
27 | a:focus,
28 | a:active {
29 | color: ${theme.colors.red};
30 | text-decoration: none;
31 | }
32 |
33 | a:hover {
34 | color: ${theme.colors.red};
35 | text-decoration: none;
36 | }
37 |
38 | .fullOpacity {
39 | opacity: 1;
40 | }
41 |
42 | code,
43 | pre {
44 | font-family: ${theme.fonts.mono};
45 | background: rgb(246, 248, 250);
46 | padding: 2px 6px;
47 | border-radius: 5px;
48 | }
49 | `;
50 |
51 | return ;
52 | };
53 |
54 | export default GlobalStyle;
55 |
--------------------------------------------------------------------------------
/src/themes/index.js:
--------------------------------------------------------------------------------
1 | import { px, breakpoints } from 'utils/css-utils';
2 |
3 | const space = [0, 4, 8, 16, 32, 48, 64, 96, 128];
4 | const spacePx = space.map(px);
5 |
6 | const fontSizes = [12, 16, 20, 24, 36, 48, 54];
7 | const fontSizesPx = fontSizes.map(px);
8 | const breakpointsPx = Object.values(breakpoints).map(px);
9 |
10 | const base = {
11 | fonts: {
12 | sans:
13 | "'nt-dapper', -apple-system, system-ui, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Ubuntu, Arial, sans-serif",
14 | mono: "'jetbrains-mono', monospace",
15 | },
16 | maxWidth: '1200px',
17 | maxWidthBig: '1800px',
18 | space,
19 | spacePx,
20 | fontSizes,
21 | fontSizesPx,
22 | breakpoints: breakpointsPx,
23 | boxShadow: '0px 16px 64px rgba(26, 25, 43, 0.32);',
24 | };
25 |
26 | export const baseColors = {
27 | orange: '#FF6700',
28 | blue: '#0041D0',
29 | red: '#FF0072',
30 | redLighten15: '#FF2687',
31 | redLighten30: '#FE4C9C',
32 | redLighten60: '#FF99C6',
33 | redLighten90: '#FFE5F1',
34 | purple: '#784BE8',
35 | mint: '#00D7CA',
36 | lightGrey: '#D9D9D9',
37 | textDark: '#1A192B',
38 | textLight: '#ffffff',
39 | violet: '#1A192B',
40 | violetLighten5: '#222138',
41 | violetLighten15: '#333154',
42 | violetLighten30: '#4C497E',
43 | violetLighten45: '#6865A5',
44 | violetLighten60: '#918FBE',
45 | violetLighten85: '#D6D5E6',
46 | violetLighten95: '#F1F1F6',
47 | silver: '#EEF0F2',
48 | silverDarken15: '#C5CBD2',
49 | silverDarken30: '#9CA8B3',
50 | silverDarken60: '#53606C',
51 | silverDarken75: '#343C43',
52 | silverLighten15: '#F0F2F3',
53 | silverLighten30: '#F3F4F5',
54 | silverLighten60: '#F8F9F9',
55 | };
56 |
57 | export const darkColors = {
58 | background: baseColors.violet,
59 | sectionBackground: baseColors.violetLighten5,
60 | cardBackground: '#222138',
61 | text: baseColors.textLight,
62 | textLight: baseColors.silverDarken30,
63 | textInverted: '#16152E',
64 | stroke: '#2E2D3F',
65 | button: '#ffffff',
66 | footerBackground: baseColors.silverLighten60,
67 | };
68 |
69 | export const lightColors = {
70 | background: '#ffffff',
71 | sectionBackground: '#f2f2f2',
72 | cardBackground: '#eeeeee',
73 | text: baseColors.textDark,
74 | textLight: '#808080',
75 | textInverted: '#ffffff',
76 | stroke: '#E9E9E9',
77 | button: baseColors.violet,
78 | footerBackground: baseColors.violet,
79 | };
80 |
81 | export const colors = {
82 | ...baseColors,
83 | dark: darkColors,
84 | light: lightColors,
85 | };
86 |
87 | const dark = {
88 | ...base,
89 | name: 'dark',
90 | colors: {
91 | ...colors,
92 | ...colors.dark,
93 | },
94 | };
95 |
96 | const light = {
97 | ...base,
98 | name: 'light',
99 | colors: {
100 | ...colors,
101 | ...colors.light,
102 | },
103 | };
104 |
105 | export default {
106 | dark,
107 | light,
108 | };
109 |
--------------------------------------------------------------------------------
/src/themes/normalize.js:
--------------------------------------------------------------------------------
1 | import React, { memo } from 'react';
2 | import { Global, css, jsx } from '@emotion/react';
3 | import emotionNormalize from 'emotion-normalize';
4 |
5 | const NormalizeStyle = memo(() => {
6 | const globalStyles = css`
7 | ${emotionNormalize}
8 |
9 | input {
10 | box-sizing: border-box;
11 | }
12 | `;
13 |
14 | return ;
15 | });
16 |
17 | export default NormalizeStyle;
18 |
--------------------------------------------------------------------------------
/src/utils/browser-utils.js:
--------------------------------------------------------------------------------
1 | export function isOldIE() {
2 | if (typeof navigator === 'undefined') {
3 | return false;
4 | }
5 |
6 | // https://stackoverflow.com/a/22242528
7 | return (
8 | navigator.userAgent.indexOf('MSIE') !== -1 ||
9 | navigator.appVersion.indexOf('Trident/') > -1
10 | );
11 | }
12 |
13 | export default {
14 | isOldIE,
15 | };
16 |
--------------------------------------------------------------------------------
/src/utils/css-utils.js:
--------------------------------------------------------------------------------
1 | import { css, jsx } from '@emotion/react';
2 |
3 | export const breakpoints = {
4 | s: 460,
5 | m: 768,
6 | l: 1024,
7 | xl: 1280,
8 | };
9 |
10 | export const device = {
11 | phone: `(min-width: ${breakpoints.s}px)`,
12 | tablet: `(min-width: ${breakpoints.m}px)`,
13 | desktop: `(min-width: ${breakpoints.l}px)`,
14 | desktopL: `(min-width: ${breakpoints.xl}px)`,
15 | };
16 |
17 | // https://github.com/styled-components/styled-components/blob/master/packages/styled-components/docs/tips-and-tricks.md#more-powerful-example
18 | const getMediaQuery = (size) => {
19 | return (...styleDefinition) => css`
20 | @media (min-width: ${size}px) {
21 | ${css(...styleDefinition)}
22 | }
23 | `;
24 | };
25 |
26 | export const media = {
27 | s: getMediaQuery(breakpoints.s),
28 | m: getMediaQuery(breakpoints.m),
29 | l: getMediaQuery(breakpoints.l),
30 | xl: getMediaQuery(breakpoints.xl),
31 | };
32 |
33 | export const rgba = (hex, alpha) => {
34 | const [r, g, b] = hex.match(/\w\w/g).map((x) => parseInt(x, 16));
35 | return `rgba(${r},${g},${b},${alpha})`;
36 | };
37 |
38 | export const px = (val) => `${val}px`;
39 |
40 | export const getThemeColor = (colorName) => (props) =>
41 | props.theme.colors[colorName];
42 |
43 | export const getThemeSpacePx = (spaceIndex) => (props) =>
44 | props.theme.spacePx[spaceIndex];
45 |
46 | export default {
47 | rgba,
48 | px,
49 | getThemeColor,
50 | getThemeSpacePx,
51 | };
52 |
--------------------------------------------------------------------------------
/src/utils/project-utils.js:
--------------------------------------------------------------------------------
1 | export const parseProjectDate = (dateString) => {
2 | const parts = dateString.split('/');
3 | return new Date(+parts[0], +parts[1] - 1, +parts[2]);
4 | };
5 |
6 | export const groupProjectsByYear = (projects) => {
7 | return projects.reduce((grouped, project) => {
8 | const year = project.date.getFullYear();
9 | grouped[year] = grouped.hasOwnProperty(year)
10 | ? grouped[year].concat([project])
11 | : [project];
12 | return grouped;
13 | }, {});
14 | };
15 |
16 | export const getMainCategory = (categoryArray = []) => {
17 | const matchingCategory = mainTags.find((mc) =>
18 | categoryArray.find((cat) => mc.id === cat)
19 | );
20 |
21 | return matchingCategory ? matchingCategory.id : null;
22 | };
23 |
24 | export const shuffleProjects = (projects) => {
25 | return projects.sort((a, b) => Math.random() - 0.5);
26 | };
27 |
28 | export default {
29 | parseProjectDate,
30 | groupProjectsByYear,
31 | getMainCategory,
32 | shuffleProjects,
33 | };
34 |
--------------------------------------------------------------------------------
/static/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xyflow/react-flow-docs-v9/4ab1ff8e6dfbf94c31868f913f6338d57c83667b/static/favicon.ico
--------------------------------------------------------------------------------
/static/fonts/JetBrainsMono-Bold.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xyflow/react-flow-docs-v9/4ab1ff8e6dfbf94c31868f913f6338d57c83667b/static/fonts/JetBrainsMono-Bold.eot
--------------------------------------------------------------------------------
/static/fonts/JetBrainsMono-Bold.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xyflow/react-flow-docs-v9/4ab1ff8e6dfbf94c31868f913f6338d57c83667b/static/fonts/JetBrainsMono-Bold.woff
--------------------------------------------------------------------------------
/static/fonts/JetBrainsMono-Bold.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xyflow/react-flow-docs-v9/4ab1ff8e6dfbf94c31868f913f6338d57c83667b/static/fonts/JetBrainsMono-Bold.woff2
--------------------------------------------------------------------------------
/static/fonts/JetBrainsMono-Regular.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xyflow/react-flow-docs-v9/4ab1ff8e6dfbf94c31868f913f6338d57c83667b/static/fonts/JetBrainsMono-Regular.eot
--------------------------------------------------------------------------------
/static/fonts/JetBrainsMono-Regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xyflow/react-flow-docs-v9/4ab1ff8e6dfbf94c31868f913f6338d57c83667b/static/fonts/JetBrainsMono-Regular.woff
--------------------------------------------------------------------------------
/static/fonts/JetBrainsMono-Regular.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xyflow/react-flow-docs-v9/4ab1ff8e6dfbf94c31868f913f6338d57c83667b/static/fonts/JetBrainsMono-Regular.woff2
--------------------------------------------------------------------------------
/static/images/react-flow-header.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xyflow/react-flow-docs-v9/4ab1ff8e6dfbf94c31868f913f6338d57c83667b/static/images/react-flow-header.jpg
--------------------------------------------------------------------------------
/static/robots.txt:
--------------------------------------------------------------------------------
1 | User-agent: *
2 | Disallow:
3 |
--------------------------------------------------------------------------------