Skip to content

Commit

Permalink
Adding Canvas component and CanvasControls component
Browse files Browse the repository at this point in the history
  • Loading branch information
Antonio Russo committed Nov 25, 2020
1 parent 5d66b0f commit cb64972
Show file tree
Hide file tree
Showing 71 changed files with 965 additions and 1,092 deletions.
6 changes: 6 additions & 0 deletions .babelrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"presets": [
"@babel/preset-env",
"@babel/preset-react"
]
}
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ jobs:
github-token: ${{ secrets.GITHUB_TOKEN }}

- name: Prepare distribution
run: cp -r ./@types ./dist/@types && cp package.json index.d.ts README.md LICENSE CHANGELOG.md CONTRIBUTING.md CODE_OF_CONDUCT.md ./dist
run: cp -r ./@types ./dist/@types && cp package.json index.d.ts CanvasControls.md LICENSE CHANGELOG.md CONTRIBUTING.md CODE_OF_CONDUCT.md ./dist

- name: Publish
run: |
Expand Down
53 changes: 53 additions & 0 deletions @types/Canvas.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { memo, ReactElement } from 'react';

export type PanState = { x: number, y: number };

export type CanvasProps = {
/**
* Since Canvas is a controlled component, the 'pan' prop defines the canvas panning
*/
pan?: PanState,
/**
* Since Canvas is a controlled component, the 'onPanChange' prop is the change handler of the 'pan' prop
*/
onPanChange?: (panState: PanState) => unknown,
/**
* Since Canvas is a controlled component, the 'zoom' prop defines its zoom level, aka: how much the canvas is scaling
*/
zoom?: number,
/**
* Since Canvas is a controlled component, the 'onZoomChange' prop is the change handler of the 'zoom' prop
*/
onZoomChange?: (zoom: number) => unknown,
/**
* Allow to zoom in/out on mouse wheel
*/
zoomOnWheel?: boolean,
/**
* The maximum allowed zoom
*/
maxZoom?: number,
/**
* The minimum allowed zoom
*/
minZoom?: number,
/**
* Defines whether the zoom should be reset on double click
*/
zoomResetOnDblClick?: boolean,
/**
* Defines whether the canvas should apply inertia when the drag is over
*/
inertia?: boolean,
/**
* Displays debug info
*/
debug?: boolean,
GridRenderer?: ReactElement,
ElementRenderer?: ReactElement,
}


declare const Canvas: (props: CanvasProps) => JSX.Element;

export default memo(Canvas);
20 changes: 20 additions & 0 deletions @types/CanvasControls.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { memo, ElementType } from 'react';
import { PanState } from './Canvas';


export type CanvasControlsProps = {
placement?: 'top-left' | 'top-right' | 'top-center' | 'bottom-right' | 'bottom-center' | 'bottom-left' | 'left' | 'right',
alignment?: 'vertical' | 'horizontal',
onPanChange?: (panState: PanState) => unknown,
onZoomChange?: (zoom: PanState) => unknown,
ButtonRender?: ElementType,
ZoomInBtnRender?: ElementType,
CenterBtnRender?: ElementType,
ZoomOutBtnRender?: ElementType,
ElementRender?: ElementType,
}


declare const CanvasControls: (props: CanvasControlsProps) => JSX.Element;

export default memo(CanvasControls);
16 changes: 16 additions & 0 deletions @types/useCanvasState.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { PanState } from './Canvas';


export type CanvasMethods = {
onPanChange: (panState: PanState) => unknown,
onZoomChange: (zoom: number) => unknown,
}

export type CanvasStates = {
pan: PanState,
zoom: number,
}

declare const useCanvasState: (initialStates?: CanvasStates) => [CanvasStates, CanvasMethods];

export default useCanvasState;
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -134,3 +134,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

- First implementation of draggable canvas
- First implementation of zoomable canvas

## [0.6.0] - 2020-11-24

### Added

- Canvas Component for panning and zooming
- useCanvas hook
- CanvasControl component
55 changes: 55 additions & 0 deletions docs/Basic-usage.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
To start using the library import a `Canvas` and a `Diagram` component, both are [controlled components](https://reactjs.org/docs/forms.html#controlled-components)
so you'll need to provide a [state](https://reactjs.org/docs/faq-state.html) and a [state handler](https://reactjs.org/docs/faq-state.html#how-do-i-update-state-with-values-that-depend-on-the-current-state)
(*beautiful-react-diagrams* exports mainly controlled components).

A *Diagram* component needs to be wrapped into a *Canvas* which allows panning/zooming functionality.<br />

A *Diagram* can easily be represented by a "*schema*" (the library provides a set of pre-made utilities to define and validate schemas).
A "*schema*" is a plain object having, at least, a "*nodes*" property defined.<br />

The "*nodes*" property must be an array of tuples (objects) described by a unique "*id*" (if not provided the library will create a unique id for the node),
a "*content*" property (can be a React component) and a "*coordinates*" property describing the node position.

Optionally a "*links*" property can be defined to define links between the nodes, similar to the "*nodes*" property it must
be an array of valid link describing tuples, a valid link must have an "*input*" and an "*output*" property.

In order to avoid unnecessary complexity the `useSchema`, `useCanvasState` hooks have been provided together with the
`createSchema` utility.

```js
import Diagram, { Canvas, createSchema, useSchema, useCanvasState, CanvasControls } from 'beautiful-react-diagrams';

// the diagram model
const initialSchema = createSchema({
nodes: [
{ id: 'node-1', content: 'Hey Jude', coordinates: [312, 27], },
{ id: 'node-2', content: 'Don\'t', coordinates: [330, 90], },
{ id: 'node-3', content: 'be afraid', coordinates: [100, 320], },
{ id: 'node-4', content: 'let me down', coordinates: [306, 332], },
{ id: 'node-5', content: 'make it bad', coordinates: [515, 330], },
],
links: [
{ input: 'node-1', output: 'node-2' },
{ input: 'node-2', output: 'node-3' },
{ input: 'node-2', output: 'node-4' },
{ input: 'node-2', output: 'node-5' },
]
});

const DiagramExample = () => {
const [canvasState, handlers] = useCanvasState(); // creates canvas state
const [schema, { onChange }] = useSchema(initialSchema); // creates diagrams schema

return (
<div style={{ height: '30rem' }}>
<Canvas {...canvasState} {...handlers}>
<Diagram schema={schema} onChange={onChange} />
<CanvasControls />
</Canvas>
</div>
);
};

<DiagramExample />
```

36 changes: 36 additions & 0 deletions docs/CanvasControls.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
```js
import Diagram, { Canvas, createSchema, useSchema, useCanvasState, CanvasControls } from 'beautiful-react-diagrams';

// the diagram model
const initialSchema = createSchema({
nodes: [
{ id: 'node-1', content: 'Hey Jude', coordinates: [312, 27], },
{ id: 'node-2', content: 'Don\'t', coordinates: [330, 90], },
{ id: 'node-3', content: 'be afraid', coordinates: [100, 320], },
{ id: 'node-4', content: 'let me down', coordinates: [306, 332], },
{ id: 'node-5', content: 'make it bad', coordinates: [515, 330], },
],
links: [
{ input: 'node-1', output: 'node-2' },
{ input: 'node-2', output: 'node-3' },
{ input: 'node-2', output: 'node-4' },
{ input: 'node-2', output: 'node-5' },
]
});

const DiagramExample = () => {
const [canvasState, handlers] = useCanvasState(); // creates canvas state
const [schema, { onChange }] = useSchema(initialSchema); // creates diagrams schema

return (
<div style={{ height: '30rem' }}>
<Canvas {...canvasState} {...handlers}>
<Diagram schema={schema} onChange={onChange} />
<CanvasControls alignment="horizontal" placement="bottom-center" />
</Canvas>
</div>
);
};

<DiagramExample />
```
120 changes: 120 additions & 0 deletions docs/Links-Ports.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
### Ports

```js
import Diagram, { Canvas, createSchema, useSchema, useCanvasState, CanvasControls } from 'beautiful-react-diagrams';

const initialSchema = createSchema({
nodes: [
{
id: 'node-1',
content: 'Start',
coordinates: [100, 150],
outputs: [
{ id: 'port-1', alignment: 'right' },
{ id: 'port-2', alignment: 'right' },
],
disableDrag: true,
data: {
foo: 'bar',
count: 0,
}
},
{
id: 'node-2',
content: 'Middle',
coordinates: [300, 150],
inputs: [
{ id: 'port-3', alignment: 'left' },
{ id: 'port-4', alignment: 'left' },
],
outputs: [
{ id: 'port-5', alignment: 'right' },
{ id: 'port-6', alignment: 'right' },
],
data: {
bar: 'foo',
}
},
{
id: 'node-3',
content: 'End',
coordinates: [600, 150],
inputs: [
{ id: 'port-7', alignment: 'left' },
{ id: 'port-8', alignment: 'left' },
],
data: {
foo: true,
bar: false,
some: {
deep: {
object: true,
}
},
}
},
],
links: [
{ input: 'port-1', output: 'port-4' },
]
});

const UncontrolledDiagram = () => {
const [canvasState, handlers] = useCanvasState(); // creates canvas state
const [schema, { onChange }] = useSchema(initialSchema); // creates diagrams schema

return (
<div style={{ height: '30rem' }}>
<Canvas {...canvasState} {...handlers}>
<Diagram schema={schema} onChange={onChange} />
<CanvasControls />
</Canvas>
</div>
);
};

<UncontrolledDiagram />
```

### Readonly Links

```js static
import Diagram, { Canvas, createSchema, useSchema, useCanvasState, CanvasControls } from 'beautiful-react-diagrams';

// the diagram model
const initialSchema = createSchema({
nodes: [
{ id: 'node-1', content: 'Hey Jude', coordinates: [312, 27], },
{ id: 'node-2', content: 'Don\'t', coordinates: [330, 90], },
{ id: 'node-3', content: 'be afraid', coordinates: [100, 320], },
{ id: 'node-4', content: 'let me down', coordinates: [306, 332], },
{ id: 'node-5', content: 'make it bad', coordinates: [515, 330], },
{ id: 'node-6', content: 'Take a sad song', coordinates: [295, 460], },
],
links: [
{ input: 'node-1', output: 'node-2', readonly: true, className: 'my-custom-link-class' },
{ input: 'node-2', output: 'node-3', readonly: true },
{ input: 'node-2', output: 'node-4', readonly: true },
{ input: 'node-2', output: 'node-5', readonly: true },
{ input: 'node-3', output: 'node-6', readonly: true },
{ input: 'node-4', output: 'node-6', readonly: true },
{ input: 'node-5', output: 'node-6', readonly: true },
]
});

const DiagramExample = () => {
const [canvasState, handlers] = useCanvasState(); // creates canvas state
const [schema, { onChange }] = useSchema(initialSchema); // creates diagrams schema

return (
<div style={{ height: '35rem' }}>
<Canvas {...canvasState} {...handlers}>
<Diagram schema={schema} onChange={onChange} />
<CanvasControls />
</Canvas>
</div>
);
};

<DiagramExample />
```
2 changes: 1 addition & 1 deletion docs/schema.md → docs/Schema-utils.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Managing complex and large schemas could be a problem, for this reason a set of function to handle the schema object
Managing complex and large schemas could be a hard thing to do, for this reason a set of function to handle the schema
comes with the library.

### createSchema
Expand Down
39 changes: 0 additions & 39 deletions docs/basic-usage.md

This file was deleted.

0 comments on commit cb64972

Please sign in to comment.