1600376640
![]() |
![]() |
---|
The library was designed to create aesthetic, animated (so far only linear) charts based on a given input. The main assumptions of the library were to create smooth transitions between subsequent data sets. For this reason, we have discovered a shortage of existing libraries related to the charts. The current package was created as part of the Rainbow.me project and for this reason it was not designed as a complete and comprehensive solution for displaying various types of charts. However, we will be now using more charts in the whole application, so we believe that the number of functionalities in the application will gradually grow.
Additionally, we are open to new Pull Requests. We want this library to become popular and complete thanks to community activity.
It’s a part of the Rainbow.me project.
The library has been released in a production-ready version. We use it inside the Rainbow.me project so it’s verified for use in production. However, it relies on React Native Reanimated 2.0 in the alpha version thus it might not work perfectly. Test it deeply before using it. Until the stable release of Reanimated, I think it’s worth not marking this library as stable. Although the library works with Reanimated without any changes, we faced a few issues related to our (quite advanced) usage of the library. Thus we made some hacks we’re not very proud of and it’s for 99% something you should not do. However, if you see some crashes, you may try one of our hacks.
There’re a few things left to make it polished regarding linear charts:
ChartProvider
and ChartPath
have been split for two components to separated responsibilities of providing data and displaying charts. I’m still not sure if it’s a good move so we can decide to move some props from one to another or connect them inside one component.springConfig
and timingConfig
)yarn add @rainbow-me/animated-charts
npm i @rainbow-me/animated-charts
yarn add react-native-haptic-feedback
npm i react-native-haptic-feedback
The library is verified on 2.0.0-alpha.6
version of reanimated.
Using TurboModules might have an impact on your current development flow and most likely you don’t want to decrease your DX. Since we’re not using reanimated in other places in the app, we made some tweaks to disable charts in development mode with compilation macros on iOS. You can find it here
Also, because we’re using libraries which currently do not support reanimated 2, we patched exports in reanimated
Furthermore, we found few differences in how the Animated
module works with and without TurboModules support, so we made a trick to fallback to the not-TM version of Animated.
Most likely, you don’t need any of those patches.
We made a generic example to show briefly what’s possible to achieve with this library. A Real-life example is available inside Rainbow!
In order to run an example clone this repo and navigate to Example
then:
yarn && cd ios && pod install && cd ..
react-native run-android
react-native run-ios
The library has been designed to provide as much flexibility as possible with the component-based API for easy integration with existing applications.
import React from 'react';
import {Dimensions, View} from 'react-native';
import {ChartDot, ChartPath, ChartPathProvider, monotoneCubicInterpolation} from '@rainbow-me/animated-charts';
export const {width: SIZE} = Dimensions.get('window');
export const data = [
{x: 1453075200, y: 1.47}, {x: 1453161600, y: 1.37},
{x: 1453248000, y: 1.53}, {x: 1453334400, y: 1.54},
{x: 1453420800, y: 1.52}, {x: 1453507200, y: 2.03},
{x: 1453593600, y: 2.10}, {x: 1453680000, y: 2.50},
{x: 1453766400, y: 2.30}, {x: 1453852800, y: 2.42},
{x: 1453939200, y: 2.55}, {x: 1454025600, y: 2.41},
{x: 1454112000, y: 2.43}, {x: 1454198400, y: 2.20},
];
const points = monotoneCubicInterpolation(data)(40);
const BasicExample = () => (
<View style={{ backgroundColor: 'black' }}>
<ChartPathProvider data={{ points, smoothingStrategy: 'bezier' }}>
<ChartPath height={SIZE / 2} stroke="yellow" width={SIZE} />
<ChartDot style={{ backgroundColor: 'blue' }} />
</ChartPathProvider>
</View>
);
The code above generates the chart below:
The whole chart’s structure has to be wrapped with ChartProvider
. It’s responsible for data managing and itself does not have a visual impact on the layout. Under the hood, it uses context API to simplify manipulation with other components. The rule is to use one data series for each wrapper.
Prop name | type | default / obligatory | description |
---|---|---|---|
softMargin |
number |
0 |
While scrubbing the chart touching edges of the screen you may want make points on the edges more accessible. With softMargin it’s possible to access points on edges doubling the speed of scrubbing beyond this margin. |
enableHaptics |
boolean |
false |
On pressing in/out on the chart it might be expected to make haptic feedback. It will happen with enableHaptics set to true and react-native-haptic-feedback installed |
data |
`{ points: [Point], nativePoints: [Point], smoothingStrategy?: ‘bezier’ | ‘simple’ | ‘complex’, smoothingFactor }` |
springConfig |
object | {damping: 15, mass: 1, stiffness: 600} |
Object defining the spring animation. This spring is used for a dot’s scale. |
timingFeedbackConfig |
object | {duration: 80} |
Object defining the timing animation. timingFeedbackConfig is used for a path’s opacity and width. |
timingAnimationConfig |
object | {duration: 300} |
Object defining the timing animation. timingAnimationConfig is used for the transition between chart’s data. |
data
is an array containing points to be displayed. A Point
is an object containing x
and y
as a number.nativeData
is an array of points that will not be drawn. However, if you used some strategy of interpolating data or simplifying you might want to present data slightly different from the real one. Then if you’d like labels to be fully correct you may want to provide real data before adjusting them.smoothingStrategy
. While presenting points path can be drawn with different approaches.
smoothingStrategy
is not provided (or set to any other value but for listed here), connects points using linear interpolation.bezier
strategy connects points with a bezier path inspired by d3 shape. It’s not parametrized by smoothingFactor
.complex
strategy uses approach explained here using cubic splines. It’s parametrized by smoothingFactor
.simple
strategy is a bit simplified complex
strategy using quadratic splines. It’s parametrized by smoothingFactor
.smoothingFactor
. Is a value from 0
to 1
defining how smooth a presentation should be. 0
means no smoothing, and it’s the default. smoothingFactor
has an impact if smoothingStrategy
is simple
or complex
.This component is used for showing the path itself.
Prop name | type | default / obligatory | description |
---|---|---|---|
disableSmoothingWhileTransitioning |
number |
false |
Although smoothing is not complex computing, it might impact performance in some low-end devices so while having a big set of data it might be worth disable smoothing while transitioning. |
height |
number |
obligatory | Height od the SVG canvas |
width |
number |
obligatory | Width od the SVG canvas |
strokeWidth |
number |
1 |
Width of the path. |
strokeWidthSelected |
number |
1 |
Width of the path selected. |
gestureEnabled |
boolean |
true |
Defines if interaction with the chart should be allowed or not |
longPressGestureHandlerProps |
object |
{maxDist: 100000, minDurationMs: 0, shouldCancelWhenOutside: false} |
Under the hood we’re using LongPressGestureHandler for handling interactions. It’s recommended to not override its props. However, it might be useful while interacting with another GH. |
selectedOpacity |
number |
0.7 |
Target opacity of the path while touching the chart. |
…rest | object |
{} |
Props applied to SVG Path. |
Component for displaying the dot for scrubbing on the chart.
Prop name | type | default | description |
---|---|---|---|
size |
number |
10 |
Size of the dot. |
…props | object |
{} |
Rest of the props applied to Reanimated.View including style |
&
ChartXLabelLabels are useful while moving finger through the chart to show the exact value in given point.
Prop name | type | default | description |
---|---|---|---|
format |
reanimated worklet | a => a |
Worklet for formatting data from the chart. It can be useful when your data is a timestamp or currency. |
…props | object |
{} |
Rest of the props applied to TextInput including style |
TODO
TODO
It’s not essential in the library, but we have decided to include a lot of helpers we are (or we were) using for displaying charts.
We have two interpolators which share the most of the API: bSplineInterpolation
and monotoneCubicInterpolation
.
import { bSplineInterpolation as interpolator } from '@rainbow-me/animated-charts';
// import { monotoneCubicInterpolation as interpolator } from '@rainbow-me/animated-charts';
const interpolatedData = interpolator(data)(80, true, false);
Code above generates 80 equidistant points from given dataset. Interpolator (monotoneCubicInterpolation
or bSplineInterpolation
) returns generator. Generator accepts 3 arguments: range
, includeExtremes
, removePointsSurroundingExtremes
.
range
is the number of points of the output.
includeExtremes
. If it’s vital to include extremes in the output, set to true. However, the data might not be fully equidistant.
removePointsSurroundingExtremes
. Makes sense only if includeExtremes
set to true
. When disabled, it might be possible that extremes look very “pointy”. To get rid of this, you can remove points surrounding extremes. E.g.
removePointsSurroundingExtremes = false
o---------o----Min--o---------o---------o---------o---------o
removePointsSurroundingExtremes = true
o--------------Min------------o---------o---------o---------o
bSplineInterpolation(data, degree = 3)
bSplineInterpolation
is inspired by victorian lib and uses B-spline interpolation of a given degree
.
monotoneCubicInterpolation(data, degree = 3)
This curve is inspired by d3 shape. "Produces a cubic spline that preserves monotonicity in y, assuming monotonicity in x, as proposed by Steffen in A simple method for monotonic interpolation in one dimension: “a smooth curve with continuous first-order derivatives that passes through any given set of data points without spurious oscillations. Local extrema can occur only at grid points where they are given by the data, but not in between two adjacent grid points.”
simplifyData(data, pickRange = 10, includeExtremes = true)
This helper takes only one point per pickRange
. Might be useful for very dense data. If it’s important, it’s possible to include extremes with the includeExtremes
flag. E.g.
pickRange = 3, includeExtremes = true
X are equidistant in this case
Y:0 1 7 2 -3 0 1 2
S----------o----------E----------X----------E----------o----------X----------o----------S
X
- points picked because index%3=0
S
– the first and the last points are always included.
E
– extremes.
Author: rainbow-me
Source Code: https://github.com/rainbow-me/react-native-animated-charts
#react-native #react #mobile-apps
1651383480
This serverless plugin is a wrapper for amplify-appsync-simulator made for testing AppSync APIs built with serverless-appsync-plugin.
Install
npm install serverless-appsync-simulator
# or
yarn add serverless-appsync-simulator
Usage
This plugin relies on your serverless yml file and on the serverless-offline
plugin.
plugins:
- serverless-dynamodb-local # only if you need dynamodb resolvers and you don't have an external dynamodb
- serverless-appsync-simulator
- serverless-offline
Note: Order is important serverless-appsync-simulator
must go before serverless-offline
To start the simulator, run the following command:
sls offline start
You should see in the logs something like:
...
Serverless: AppSync endpoint: http://localhost:20002/graphql
Serverless: GraphiQl: http://localhost:20002
...
Configuration
Put options under custom.appsync-simulator
in your serverless.yml
file
| option | default | description | | ------------------------ | -------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------- | | apiKey | 0123456789
| When using API_KEY
as authentication type, the key to authenticate to the endpoint. | | port | 20002 | AppSync operations port; if using multiple APIs, the value of this option will be used as a starting point, and each other API will have a port of lastPort + 10 (e.g. 20002, 20012, 20022, etc.) | | wsPort | 20003 | AppSync subscriptions port; if using multiple APIs, the value of this option will be used as a starting point, and each other API will have a port of lastPort + 10 (e.g. 20003, 20013, 20023, etc.) | | location | . (base directory) | Location of the lambda functions handlers. | | refMap | {} | A mapping of resource resolutions for the Ref
function | | getAttMap | {} | A mapping of resource resolutions for the GetAtt
function | | importValueMap | {} | A mapping of resource resolutions for the ImportValue
function | | functions | {} | A mapping of external functions for providing invoke url for external fucntions | | dynamoDb.endpoint | http://localhost:8000 | Dynamodb endpoint. Specify it if you're not using serverless-dynamodb-local. Otherwise, port is taken from dynamodb-local conf | | dynamoDb.region | localhost | Dynamodb region. Specify it if you're connecting to a remote Dynamodb intance. | | dynamoDb.accessKeyId | DEFAULT_ACCESS_KEY | AWS Access Key ID to access DynamoDB | | dynamoDb.secretAccessKey | DEFAULT_SECRET | AWS Secret Key to access DynamoDB | | dynamoDb.sessionToken | DEFAULT_ACCESS_TOKEEN | AWS Session Token to access DynamoDB, only if you have temporary security credentials configured on AWS | | dynamoDb.* | | You can add every configuration accepted by DynamoDB SDK | | rds.dbName | | Name of the database | | rds.dbHost | | Database host | | rds.dbDialect | | Database dialect. Possible values (mysql | postgres) | | rds.dbUsername | | Database username | | rds.dbPassword | | Database password | | rds.dbPort | | Database port | | watch | - *.graphql
- *.vtl | Array of glob patterns to watch for hot-reloading. |
Example:
custom:
appsync-simulator:
location: '.webpack/service' # use webpack build directory
dynamoDb:
endpoint: 'http://my-custom-dynamo:8000'
Hot-reloading
By default, the simulator will hot-relad when changes to *.graphql
or *.vtl
files are detected. Changes to *.yml
files are not supported (yet? - this is a Serverless Framework limitation). You will need to restart the simulator each time you change yml files.
Hot-reloading relies on watchman. Make sure it is installed on your system.
You can change the files being watched with the watch
option, which is then passed to watchman as the match expression.
e.g.
custom:
appsync-simulator:
watch:
- ["match", "handlers/**/*.vtl", "wholename"] # => array is interpreted as the literal match expression
- "*.graphql" # => string like this is equivalent to `["match", "*.graphql"]`
Or you can opt-out by leaving an empty array or set the option to false
Note: Functions should not require hot-reloading, unless you are using a transpiler or a bundler (such as webpack, babel or typescript), un which case you should delegate hot-reloading to that instead.
Resource CloudFormation functions resolution
This plugin supports some resources resolution from the Ref
, Fn::GetAtt
and Fn::ImportValue
functions in your yaml file. It also supports some other Cfn functions such as Fn::Join
, Fb::Sub
, etc.
Note: Under the hood, this features relies on the cfn-resolver-lib package. For more info on supported cfn functions, refer to the documentation
You can reference resources in your functions' environment variables (that will be accessible from your lambda functions) or datasource definitions. The plugin will automatically resolve them for you.
provider:
environment:
BUCKET_NAME:
Ref: MyBucket # resolves to `my-bucket-name`
resources:
Resources:
MyDbTable:
Type: AWS::DynamoDB::Table
Properties:
TableName: myTable
...
MyBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: my-bucket-name
...
# in your appsync config
dataSources:
- type: AMAZON_DYNAMODB
name: dynamosource
config:
tableName:
Ref: MyDbTable # resolves to `myTable`
Sometimes, some references cannot be resolved, as they come from an Output from Cloudformation; or you might want to use mocked values in your local environment.
In those cases, you can define (or override) those values using the refMap
, getAttMap
and importValueMap
options.
refMap
takes a mapping of resource name to value pairsgetAttMap
takes a mapping of resource name to attribute/values pairsimportValueMap
takes a mapping of import name to values pairsExample:
custom:
appsync-simulator:
refMap:
# Override `MyDbTable` resolution from the previous example.
MyDbTable: 'mock-myTable'
getAttMap:
# define ElasticSearchInstance DomainName
ElasticSearchInstance:
DomainEndpoint: 'localhost:9200'
importValueMap:
other-service-api-url: 'https://other.api.url.com/graphql'
# in your appsync config
dataSources:
- type: AMAZON_ELASTICSEARCH
name: elasticsource
config:
# endpoint resolves as 'http://localhost:9200'
endpoint:
Fn::Join:
- ''
- - https://
- Fn::GetAtt:
- ElasticSearchInstance
- DomainEndpoint
In some special cases you will need to use key-value mock nottation. Good example can be case when you need to include serverless stage value (${self:provider.stage}
) in the import name.
This notation can be used with all mocks - refMap
, getAttMap
and importValueMap
provider:
environment:
FINISH_ACTIVITY_FUNCTION_ARN:
Fn::ImportValue: other-service-api-${self:provider.stage}-url
custom:
serverless-appsync-simulator:
importValueMap:
- key: other-service-api-${self:provider.stage}-url
value: 'https://other.api.url.com/graphql'
This plugin only tries to resolve the following parts of the yml tree:
provider.environment
functions[*].environment
custom.appSync
If you have the need of resolving others, feel free to open an issue and explain your use case.
For now, the supported resources to be automatically resovled by Ref:
are:
Feel free to open a PR or an issue to extend them as well.
External functions
When a function is not defined withing the current serverless file you can still call it by providing an invoke url which should point to a REST method. Make sure you specify "get" or "post" for the method. Default is "get", but you probably want "post".
custom:
appsync-simulator:
functions:
addUser:
url: http://localhost:3016/2015-03-31/functions/addUser/invocations
method: post
addPost:
url: https://jsonplaceholder.typicode.com/posts
method: post
Supported Resolver types
This plugin supports resolvers implemented by amplify-appsync-simulator
, as well as custom resolvers.
From Aws Amplify:
Implemented by this plugin
#set( $cols = [] )
#set( $vals = [] )
#foreach( $entry in $ctx.args.input.keySet() )
#set( $regex = "([a-z])([A-Z]+)")
#set( $replacement = "$1_$2")
#set( $toSnake = $entry.replaceAll($regex, $replacement).toLowerCase() )
#set( $discard = $cols.add("$toSnake") )
#if( $util.isBoolean($ctx.args.input[$entry]) )
#if( $ctx.args.input[$entry] )
#set( $discard = $vals.add("1") )
#else
#set( $discard = $vals.add("0") )
#end
#else
#set( $discard = $vals.add("'$ctx.args.input[$entry]'") )
#end
#end
#set( $valStr = $vals.toString().replace("[","(").replace("]",")") )
#set( $colStr = $cols.toString().replace("[","(").replace("]",")") )
#if ( $valStr.substring(0, 1) != '(' )
#set( $valStr = "($valStr)" )
#end
#if ( $colStr.substring(0, 1) != '(' )
#set( $colStr = "($colStr)" )
#end
{
"version": "2018-05-29",
"statements": ["INSERT INTO <name-of-table> $colStr VALUES $valStr", "SELECT * FROM <name-of-table> ORDER BY id DESC LIMIT 1"]
}
#set( $update = "" )
#set( $equals = "=" )
#foreach( $entry in $ctx.args.input.keySet() )
#set( $cur = $ctx.args.input[$entry] )
#set( $regex = "([a-z])([A-Z]+)")
#set( $replacement = "$1_$2")
#set( $toSnake = $entry.replaceAll($regex, $replacement).toLowerCase() )
#if( $util.isBoolean($cur) )
#if( $cur )
#set ( $cur = "1" )
#else
#set ( $cur = "0" )
#end
#end
#if ( $util.isNullOrEmpty($update) )
#set($update = "$toSnake$equals'$cur'" )
#else
#set($update = "$update,$toSnake$equals'$cur'" )
#end
#end
{
"version": "2018-05-29",
"statements": ["UPDATE <name-of-table> SET $update WHERE id=$ctx.args.input.id", "SELECT * FROM <name-of-table> WHERE id=$ctx.args.input.id"]
}
{
"version": "2018-05-29",
"statements": ["UPDATE <name-of-table> set deleted_at=NOW() WHERE id=$ctx.args.id", "SELECT * FROM <name-of-table> WHERE id=$ctx.args.id"]
}
#set ( $index = -1)
#set ( $result = $util.parseJson($ctx.result) )
#set ( $meta = $result.sqlStatementResults[1].columnMetadata)
#foreach ($column in $meta)
#set ($index = $index + 1)
#if ( $column["typeName"] == "timestamptz" )
#set ($time = $result["sqlStatementResults"][1]["records"][0][$index]["stringValue"] )
#set ( $nowEpochMillis = $util.time.parseFormattedToEpochMilliSeconds("$time.substring(0,19)+0000", "yyyy-MM-dd HH:mm:ssZ") )
#set ( $isoDateTime = $util.time.epochMilliSecondsToISO8601($nowEpochMillis) )
$util.qr( $result["sqlStatementResults"][1]["records"][0][$index].put("stringValue", "$isoDateTime") )
#end
#end
#set ( $res = $util.parseJson($util.rds.toJsonString($util.toJson($result)))[1][0] )
#set ( $response = {} )
#foreach($mapKey in $res.keySet())
#set ( $s = $mapKey.split("_") )
#set ( $camelCase="" )
#set ( $isFirst=true )
#foreach($entry in $s)
#if ( $isFirst )
#set ( $first = $entry.substring(0,1) )
#else
#set ( $first = $entry.substring(0,1).toUpperCase() )
#end
#set ( $isFirst=false )
#set ( $stringLength = $entry.length() )
#set ( $remaining = $entry.substring(1, $stringLength) )
#set ( $camelCase = "$camelCase$first$remaining" )
#end
$util.qr( $response.put("$camelCase", $res[$mapKey]) )
#end
$utils.toJson($response)
Variable map support is limited and does not differentiate numbers and strings data types, please inject them directly if needed.
Will be escaped properly: null
, true
, and false
values.
{
"version": "2018-05-29",
"statements": [
"UPDATE <name-of-table> set deleted_at=NOW() WHERE id=:ID",
"SELECT * FROM <name-of-table> WHERE id=:ID and unix_timestamp > $ctx.args.newerThan"
],
variableMap: {
":ID": $ctx.args.id,
## ":TIMESTAMP": $ctx.args.newerThan -- This will be handled as a string!!!
}
}
Requires
Author: Serverless-appsync
Source Code: https://github.com/serverless-appsync/serverless-appsync-simulator
License: MIT License
1577937949
React chart is a graphical representation of data, in which “the data is represented by symbols, such as bars in a bar chart, lines in a line chart, or slices in a pie chart”. Using the right type of chart is one decision to make when presenting data but as React developers, we have our own set of concerns, mainly, choosing the right chart component library.
Here are the 10 best react charts component I’ve picked. You can view and apply to your application.
A responsive, composable react charting library with a hand-drawn style.
Features
View Demo: https://beizhedenglong.github.io/rough-charts/?path=/docs/roughcharts–page
Github: https://github.com/beizhedenglong/rough-charts
Download Link: https://github.com/beizhedenglong/rough-charts/archive/master.zip
This is a responsive ThingSpeak API grapher. It uses a JSON router to overcome CORS.
Built using ReactJS, Bootstrap and ChartJS.
View Demo: https://opens3.net/channel-grapher.html
Github: https://github.com/wilyarti/channel-grapher
Download Link: https://github.com/wilyarti/channel-grapher/archive/master.zip
Simple, immersive and interactive charts for React.
Features
View Demo: https://react-charts.js.org/examples/line
Github: https://github.com/tannerlinsley/react-charts
Download Link: https://github.com/tannerlinsley/react-charts/archive/master.zip
This project contains the implementation of libraries D3, highcharts and react-google-maps with the ReactJS. It shows how easy is adding those libraries in ReactJS.
View Demo: https://marekdano.github.io/react-charts-and-maps/
Github: https://github.com/marekdano/react-charts-and-maps
Download Link: https://github.com/marekdano/react-charts-and-maps/archive/master.zip
React Diagrams is currently getting a bit of a rewrite to enable much more advanced features.
View Demo: http://projectstorm.cloud/react-diagrams/?path=/story/simple-usage–simple-flow-example
Github: https://github.com/projectstorm/react-diagrams
Download Link: https://github.com/projectstorm/react-diagrams/archive/master.zip
Interactive and configurable graphs with react and d3 effortlessly.
View Demo: https://goodguydaniel.com/react-d3-graph/sandbox/index.html
Github: https://github.com/jcapobianco-cbi/react-d3-graph-cbi
Download Link: https://github.com/jcapobianco-cbi/react-d3-graph-cbi/archive/master.zip
React VizGrammar is a wrapper around Victory JS and it makes charting easier by adding boilerplate code so that designers and developers can get started and set it up in a few minutes.
A chart can be embedded in a React environment simply by using the VizG react component.
View Demo: https://wso2.github.io/react-vizgrammar/#/
Github: https://github.com/wso2/react-vizgrammar
Download Link: https://github.com/wso2/react-vizgrammar/archive/master.zip
preact based charting library. Written with d3-maths, and TypeScript!
View Demo: https://codesandbox.io/s/ko300qzppv
Github: http://github.com/pmkroeker/preact-charts
Download Link: https://github.com/pmkroeker/preact-charts/archive/master.zip
REAVIZ is a modular chart component library that leverages React natively for rendering the components while using D3js under the hood for calculations. The library provides an easy way to get started creating charts without sacrificing customization ability.
View Demo: https://reaviz.io/?path=/docs/docs-intro–page
Github: https://github.com/jask-oss/reaviz
Download Link: https://github.com/jask-oss/reaviz/archive/master.zip
This is React Signals Plot component for geophysical data visualization.
The component supports ‘on the fly’ data compression. That’s why you can use it for drawing line charts which contain millions of points. ReactSignalsPlot is an interactive component. You can use a mouse or touch to move and zoom.
View Demo: https://gromtech.github.io/react-signals-plot/
Github: https://github.com/gromtech/react-signals-plot
Download Link: https://github.com/gromtech/react-signals-plot/archive/develop.zip
#react #react-chart #react-chart-component #chart #chart-component
1651319520
Serverless APIGateway Service Proxy
This Serverless Framework plugin supports the AWS service proxy integration feature of API Gateway. You can directly connect API Gateway to AWS services without Lambda.
Run serverless plugin install
in your Serverless project.
serverless plugin install -n serverless-apigateway-service-proxy
Here is a services list which this plugin supports for now. But will expand to other services in the feature. Please pull request if you are intersted in it.
Define settings of the AWS services you want to integrate under custom > apiGatewayServiceProxies
and run serverless deploy
.
Sample syntax for Kinesis proxy in serverless.yml
.
custom:
apiGatewayServiceProxies:
- kinesis: # partitionkey is set apigateway requestid by default
path: /kinesis
method: post
streamName: { Ref: 'YourStream' }
cors: true
- kinesis:
path: /kinesis
method: post
partitionKey: 'hardcordedkey' # use static partitionkey
streamName: { Ref: 'YourStream' }
cors: true
- kinesis:
path: /kinesis/{myKey} # use path parameter
method: post
partitionKey:
pathParam: myKey
streamName: { Ref: 'YourStream' }
cors: true
- kinesis:
path: /kinesis
method: post
partitionKey:
bodyParam: data.myKey # use body parameter
streamName: { Ref: 'YourStream' }
cors: true
- kinesis:
path: /kinesis
method: post
partitionKey:
queryStringParam: myKey # use query string param
streamName: { Ref: 'YourStream' }
cors: true
- kinesis: # PutRecords
path: /kinesis
method: post
action: PutRecords
streamName: { Ref: 'YourStream' }
cors: true
resources:
Resources:
YourStream:
Type: AWS::Kinesis::Stream
Properties:
ShardCount: 1
Sample request after deploying.
curl https://xxxxxxx.execute-api.us-east-1.amazonaws.com/dev/kinesis -d '{"message": "some data"}' -H 'Content-Type:application/json'
Sample syntax for SQS proxy in serverless.yml
.
custom:
apiGatewayServiceProxies:
- sqs:
path: /sqs
method: post
queueName: { 'Fn::GetAtt': ['SQSQueue', 'QueueName'] }
cors: true
resources:
Resources:
SQSQueue:
Type: 'AWS::SQS::Queue'
Sample request after deploying.
curl https://xxxxxx.execute-api.us-east-1.amazonaws.com/dev/sqs -d '{"message": "testtest"}' -H 'Content-Type:application/json'
If you'd like to pass additional data to the integration request, you can do so by including your custom API Gateway request parameters in serverless.yml
like so:
custom:
apiGatewayServiceProxies:
- sqs:
path: /queue
method: post
queueName: !GetAtt MyQueue.QueueName
cors: true
requestParameters:
'integration.request.querystring.MessageAttribute.1.Name': "'cognitoIdentityId'"
'integration.request.querystring.MessageAttribute.1.Value.StringValue': 'context.identity.cognitoIdentityId'
'integration.request.querystring.MessageAttribute.1.Value.DataType': "'String'"
'integration.request.querystring.MessageAttribute.2.Name': "'cognitoAuthenticationProvider'"
'integration.request.querystring.MessageAttribute.2.Value.StringValue': 'context.identity.cognitoAuthenticationProvider'
'integration.request.querystring.MessageAttribute.2.Value.DataType': "'String'"
The alternative way to pass MessageAttribute
parameters is via a request body mapping template.
See the SQS section under Customizing request body mapping templates
Simplified response template customization
You can get a simple customization of the responses by providing a template for the possible responses. The template is assumed to be application/json
.
custom:
apiGatewayServiceProxies:
- sqs:
path: /queue
method: post
queueName: !GetAtt MyQueue.QueueName
cors: true
response:
template:
# `success` is used when the integration response is 200
success: |-
{ "message: "accepted" }
# `clientError` is used when the integration response is 400
clientError: |-
{ "message": "there is an error in your request" }
# `serverError` is used when the integration response is 500
serverError: |-
{ "message": "there was an error handling your request" }
Full response customization
If you want more control over the integration response, you can provide an array of objects for the response
value:
custom:
apiGatewayServiceProxies:
- sqs:
path: /queue
method: post
queueName: !GetAtt MyQueue.QueueName
cors: true
response:
- statusCode: 200
selectionPattern: '2\\d{2}'
responseParameters: {}
responseTemplates:
application/json: |-
{ "message": "accepted" }
The object keys correspond to the API Gateway integration response object.
Sample syntax for S3 proxy in serverless.yml
.
custom:
apiGatewayServiceProxies:
- s3:
path: /s3
method: post
action: PutObject
bucket:
Ref: S3Bucket
key: static-key.json # use static key
cors: true
- s3:
path: /s3/{myKey} # use path param
method: get
action: GetObject
bucket:
Ref: S3Bucket
key:
pathParam: myKey
cors: true
- s3:
path: /s3
method: delete
action: DeleteObject
bucket:
Ref: S3Bucket
key:
queryStringParam: key # use query string param
cors: true
resources:
Resources:
S3Bucket:
Type: 'AWS::S3::Bucket'
Sample request after deploying.
curl https://xxxxxx.execute-api.us-east-1.amazonaws.com/dev/s3 -d '{"message": "testtest"}' -H 'Content-Type:application/json'
Similar to the SQS support, you can customize the default request parameters serverless.yml
like so:
custom:
apiGatewayServiceProxies:
- s3:
path: /s3
method: post
action: PutObject
bucket:
Ref: S3Bucket
cors: true
requestParameters:
# if requestParameters has a 'integration.request.path.object' property you should remove the key setting
'integration.request.path.object': 'context.requestId'
'integration.request.header.cache-control': "'public, max-age=31536000, immutable'"
If you'd like use custom API Gateway request templates, you can do so like so:
custom:
apiGatewayServiceProxies:
- s3:
path: /s3
method: get
action: GetObject
bucket:
Ref: S3Bucket
request:
template:
application/json: |
#set ($specialStuff = $context.request.header.x-special)
#set ($context.requestOverride.path.object = $specialStuff.replaceAll('_', '-'))
{}
Note that if the client does not provide a Content-Type
header in the request, ApiGateway defaults to application/json
.
Added the new customization parameter that lets the user set a custom Path Override in API Gateway other than the {bucket}/{object}
This parameter is optional and if not set, will fall back to {bucket}/{object}
The Path Override will add {bucket}/
automatically in front
Please keep in mind, that key or path.object still needs to be set at the moment (maybe this will be made optional later on with this)
Usage (With 2 Path Parameters (folder and file and a fixed file extension)):
custom:
apiGatewayServiceProxies:
- s3:
path: /s3/{folder}/{file}
method: get
action: GetObject
pathOverride: '{folder}/{file}.xml'
bucket:
Ref: S3Bucket
cors: true
requestParameters:
# if requestParameters has a 'integration.request.path.object' property you should remove the key setting
'integration.request.path.folder': 'method.request.path.folder'
'integration.request.path.file': 'method.request.path.file'
'integration.request.path.object': 'context.requestId'
'integration.request.header.cache-control': "'public, max-age=31536000, immutable'"
This will result in API Gateway setting the Path Override attribute to {bucket}/{folder}/{file}.xml
So for example if you navigate to the API Gatway endpoint /language/en
it will fetch the file in S3 from {bucket}/language/en.xml
Can use greedy, for deeper Folders
The forementioned example can also be shortened by a greedy approach. Thanks to @taylorreece for mentioning this.
custom:
apiGatewayServiceProxies:
- s3:
path: /s3/{myPath+}
method: get
action: GetObject
pathOverride: '{myPath}.xml'
bucket:
Ref: S3Bucket
cors: true
requestParameters:
# if requestParameters has a 'integration.request.path.object' property you should remove the key setting
'integration.request.path.myPath': 'method.request.path.myPath'
'integration.request.path.object': 'context.requestId'
'integration.request.header.cache-control': "'public, max-age=31536000, immutable'"
This will translate for example /s3/a/b/c
to a/b/c.xml
You can get a simple customization of the responses by providing a template for the possible responses. The template is assumed to be application/json
.
custom:
apiGatewayServiceProxies:
- s3:
path: /s3
method: post
action: PutObject
bucket:
Ref: S3Bucket
key: static-key.json
response:
template:
# `success` is used when the integration response is 200
success: |-
{ "message: "accepted" }
# `clientError` is used when the integration response is 400
clientError: |-
{ "message": "there is an error in your request" }
# `serverError` is used when the integration response is 500
serverError: |-
{ "message": "there was an error handling your request" }
Sample syntax for SNS proxy in serverless.yml
.
custom:
apiGatewayServiceProxies:
- sns:
path: /sns
method: post
topicName: { 'Fn::GetAtt': ['SNSTopic', 'TopicName'] }
cors: true
resources:
Resources:
SNSTopic:
Type: AWS::SNS::Topic
Sample request after deploying.
curl https://xxxxxx.execute-api.us-east-1.amazonaws.com/dev/sns -d '{"message": "testtest"}' -H 'Content-Type:application/json'
Simplified response template customization
You can get a simple customization of the responses by providing a template for the possible responses. The template is assumed to be application/json
.
custom:
apiGatewayServiceProxies:
- sns:
path: /sns
method: post
topicName: { 'Fn::GetAtt': ['SNSTopic', 'TopicName'] }
cors: true
response:
template:
# `success` is used when the integration response is 200
success: |-
{ "message: "accepted" }
# `clientError` is used when the integration response is 400
clientError: |-
{ "message": "there is an error in your request" }
# `serverError` is used when the integration response is 500
serverError: |-
{ "message": "there was an error handling your request" }
Full response customization
If you want more control over the integration response, you can provide an array of objects for the response
value:
custom:
apiGatewayServiceProxies:
- sns:
path: /sns
method: post
topicName: { 'Fn::GetAtt': ['SNSTopic', 'TopicName'] }
cors: true
response:
- statusCode: 200
selectionPattern: '2\d{2}'
responseParameters: {}
responseTemplates:
application/json: |-
{ "message": "accepted" }
The object keys correspond to the API Gateway integration response object.
Content Handling and Pass Through Behaviour customization
If you want to work with binary fata, you can not specify contentHandling
and PassThrough
inside the request
object.
custom:
apiGatewayServiceProxies:
- sns:
path: /sns
method: post
topicName: { 'Fn::GetAtt': ['SNSTopic', 'TopicName'] }
request:
contentHandling: CONVERT_TO_TEXT
passThrough: WHEN_NO_TEMPLATES
The allowed values correspond with the API Gateway Method integration for ContentHandling and PassthroughBehavior
Sample syntax for DynamoDB proxy in serverless.yml
. Currently, the supported DynamoDB Operations are PutItem
, GetItem
and DeleteItem
.
custom:
apiGatewayServiceProxies:
- dynamodb:
path: /dynamodb/{id}/{sort}
method: put
tableName: { Ref: 'YourTable' }
hashKey: # set pathParam or queryStringParam as a partitionkey.
pathParam: id
attributeType: S
rangeKey: # required if also using sort key. set pathParam or queryStringParam.
pathParam: sort
attributeType: S
action: PutItem # specify action to the table what you want
condition: attribute_not_exists(Id) # optional Condition Expressions parameter for the table
cors: true
- dynamodb:
path: /dynamodb
method: get
tableName: { Ref: 'YourTable' }
hashKey:
queryStringParam: id # use query string parameter
attributeType: S
rangeKey:
queryStringParam: sort
attributeType: S
action: GetItem
cors: true
- dynamodb:
path: /dynamodb/{id}
method: delete
tableName: { Ref: 'YourTable' }
hashKey:
pathParam: id
attributeType: S
action: DeleteItem
cors: true
resources:
Resources:
YourTable:
Type: AWS::DynamoDB::Table
Properties:
TableName: YourTable
AttributeDefinitions:
- AttributeName: id
AttributeType: S
- AttributeName: sort
AttributeType: S
KeySchema:
- AttributeName: id
KeyType: HASH
- AttributeName: sort
KeyType: RANGE
ProvisionedThroughput:
ReadCapacityUnits: 1
WriteCapacityUnits: 1
Sample request after deploying.
curl -XPUT https://xxxxxxx.execute-api.us-east-1.amazonaws.com/dev/dynamodb/<hashKey>/<sortkey> \
-d '{"name":{"S":"john"},"address":{"S":"xxxxx"}}' \
-H 'Content-Type:application/json'
Sample syntax for EventBridge proxy in serverless.yml
.
custom:
apiGatewayServiceProxies:
- eventbridge: # source and detailType are hardcoded; detail defaults to POST body
path: /eventbridge
method: post
source: 'hardcoded_source'
detailType: 'hardcoded_detailType'
eventBusName: { Ref: 'YourBusName' }
cors: true
- eventbridge: # source and detailType as path parameters
path: /eventbridge/{detailTypeKey}/{sourceKey}
method: post
detailType:
pathParam: detailTypeKey
source:
pathParam: sourceKey
eventBusName: { Ref: 'YourBusName' }
cors: true
- eventbridge: # source, detail, and detailType as body parameters
path: /eventbridge/{detailTypeKey}/{sourceKey}
method: post
detailType:
bodyParam: data.detailType
source:
bodyParam: data.source
detail:
bodyParam: data.detail
eventBusName: { Ref: 'YourBusName' }
cors: true
resources:
Resources:
YourBus:
Type: AWS::Events::EventBus
Properties:
Name: YourEventBus
Sample request after deploying.
curl https://xxxxxxx.execute-api.us-east-1.amazonaws.com/dev/eventbridge -d '{"message": "some data"}' -H 'Content-Type:application/json'
To set CORS configurations for your HTTP endpoints, simply modify your event configurations as follows:
custom:
apiGatewayServiceProxies:
- kinesis:
path: /kinesis
method: post
streamName: { Ref: 'YourStream' }
cors: true
Setting cors to true assumes a default configuration which is equivalent to:
custom:
apiGatewayServiceProxies:
- kinesis:
path: /kinesis
method: post
streamName: { Ref: 'YourStream' }
cors:
origin: '*'
headers:
- Content-Type
- X-Amz-Date
- Authorization
- X-Api-Key
- X-Amz-Security-Token
- X-Amz-User-Agent
allowCredentials: false
Configuring the cors property sets Access-Control-Allow-Origin, Access-Control-Allow-Headers, Access-Control-Allow-Methods,Access-Control-Allow-Credentials headers in the CORS preflight response. To enable the Access-Control-Max-Age preflight response header, set the maxAge property in the cors object:
custom:
apiGatewayServiceProxies:
- kinesis:
path: /kinesis
method: post
streamName: { Ref: 'YourStream' }
cors:
origin: '*'
maxAge: 86400
If you are using CloudFront or another CDN for your API Gateway, you may want to setup a Cache-Control header to allow for OPTIONS request to be cached to avoid the additional hop.
To enable the Cache-Control header on preflight response, set the cacheControl property in the cors object:
custom:
apiGatewayServiceProxies:
- kinesis:
path: /kinesis
method: post
streamName: { Ref: 'YourStream' }
cors:
origin: '*'
headers:
- Content-Type
- X-Amz-Date
- Authorization
- X-Api-Key
- X-Amz-Security-Token
- X-Amz-User-Agent
allowCredentials: false
cacheControl: 'max-age=600, s-maxage=600, proxy-revalidate' # Caches on browser and proxy for 10 minutes and doesnt allow proxy to serve out of date content
You can pass in any supported authorization type:
custom:
apiGatewayServiceProxies:
- sqs:
path: /sqs
method: post
queueName: { 'Fn::GetAtt': ['SQSQueue', 'QueueName'] }
cors: true
# optional - defaults to 'NONE'
authorizationType: 'AWS_IAM' # can be one of ['NONE', 'AWS_IAM', 'CUSTOM', 'COGNITO_USER_POOLS']
# when using 'CUSTOM' authorization type, one should specify authorizerId
# authorizerId: { Ref: 'AuthorizerLogicalId' }
# when using 'COGNITO_USER_POOLS' authorization type, one can specify a list of authorization scopes
# authorizationScopes: ['scope1','scope2']
resources:
Resources:
SQSQueue:
Type: 'AWS::SQS::Queue'
Source: AWS::ApiGateway::Method docs
You can indicate whether the method requires clients to submit a valid API key using private
flag:
custom:
apiGatewayServiceProxies:
- sqs:
path: /sqs
method: post
queueName: { 'Fn::GetAtt': ['SQSQueue', 'QueueName'] }
cors: true
private: true
resources:
Resources:
SQSQueue:
Type: 'AWS::SQS::Queue'
which is the same syntax used in Serverless framework.
Source: Serverless: Setting API keys for your Rest API
Source: AWS::ApiGateway::Method docs
By default, the plugin will generate a role with the required permissions for each service type that is configured.
You can configure your own role by setting the roleArn
attribute:
custom:
apiGatewayServiceProxies:
- sqs:
path: /sqs
method: post
queueName: { 'Fn::GetAtt': ['SQSQueue', 'QueueName'] }
cors: true
roleArn: # Optional. A default role is created when not configured
Fn::GetAtt: [CustomS3Role, Arn]
resources:
Resources:
SQSQueue:
Type: 'AWS::SQS::Queue'
CustomS3Role:
# Custom Role definition
Type: 'AWS::IAM::Role'
The plugin allows one to specify which parameters the API Gateway method accepts.
A common use case is to pass custom data to the integration request:
custom:
apiGatewayServiceProxies:
- sqs:
path: /sqs
method: post
queueName: { 'Fn::GetAtt': ['SqsQueue', 'QueueName'] }
cors: true
acceptParameters:
'method.request.header.Custom-Header': true
requestParameters:
'integration.request.querystring.MessageAttribute.1.Name': "'custom-Header'"
'integration.request.querystring.MessageAttribute.1.Value.StringValue': 'method.request.header.Custom-Header'
'integration.request.querystring.MessageAttribute.1.Value.DataType': "'String'"
resources:
Resources:
SqsQueue:
Type: 'AWS::SQS::Queue'
Any published SQS message will have the Custom-Header
value added as a message attribute.
If you'd like to add content types or customize the default templates, you can do so by including your custom API Gateway request mapping template in serverless.yml
like so:
# Required for using Fn::Sub
plugins:
- serverless-cloudformation-sub-variables
custom:
apiGatewayServiceProxies:
- kinesis:
path: /kinesis
method: post
streamName: { Ref: 'MyStream' }
request:
template:
text/plain:
Fn::Sub:
- |
#set($msgBody = $util.parseJson($input.body))
#set($msgId = $msgBody.MessageId)
{
"Data": "$util.base64Encode($input.body)",
"PartitionKey": "$msgId",
"StreamName": "#{MyStreamArn}"
}
- MyStreamArn:
Fn::GetAtt: [MyStream, Arn]
It is important that the mapping template will return a valid
application/json
string
Source: How to connect SNS to Kinesis for cross-account delivery via API Gateway
Customizing SQS request templates requires us to force all requests to use an application/x-www-form-urlencoded
style body. The plugin sets the Content-Type
header to application/x-www-form-urlencoded
for you, but API Gateway will still look for the template under the application/json
request template type, so that is where you need to configure you request body in serverless.yml
:
custom:
apiGatewayServiceProxies:
- sqs:
path: /{version}/event/receiver
method: post
queueName: { 'Fn::GetAtt': ['SqsQueue', 'QueueName'] }
request:
template:
application/json: |-
#set ($body = $util.parseJson($input.body))
Action=SendMessage##
&MessageGroupId=$util.urlEncode($body.event_type)##
&MessageDeduplicationId=$util.urlEncode($body.event_id)##
&MessageAttribute.1.Name=$util.urlEncode("X-Custom-Signature")##
&MessageAttribute.1.Value.DataType=String##
&MessageAttribute.1.Value.StringValue=$util.urlEncode($input.params("X-Custom-Signature"))##
&MessageBody=$util.urlEncode($input.body)
Note that the ##
at the end of each line is an empty comment. In VTL this has the effect of stripping the newline from the end of the line (as it is commented out), which makes API Gateway read all the lines in the template as one line.
Be careful when mixing additional requestParameters
into your SQS endpoint as you may overwrite the integration.request.header.Content-Type
and stop the request template from being parsed correctly. You may also unintentionally create conflicts between parameters passed using requestParameters
and those in your request template. Typically you should only use the request template if you need to manipulate the incoming request body in some way.
Your custom template must also set the Action
and MessageBody
parameters, as these will not be added for you by the plugin.
When using a custom request body, headers sent by a client will no longer be passed through to the SQS queue (PassthroughBehavior
is automatically set to NEVER
). You will need to pass through headers sent by the client explicitly in the request body. Also, any custom querystring parameters in the requestParameters
array will be ignored. These also need to be added via the custom request body.
Similar to the Kinesis support, you can customize the default request mapping templates in serverless.yml
like so:
# Required for using Fn::Sub
plugins:
- serverless-cloudformation-sub-variables
custom:
apiGatewayServiceProxies:
- kinesis:
path: /sns
method: post
topicName: { 'Fn::GetAtt': ['SNSTopic', 'TopicName'] }
request:
template:
application/json:
Fn::Sub:
- "Action=Publish&Message=$util.urlEncode('This is a fixed message')&TopicArn=$util.urlEncode('#{MyTopicArn}')"
- MyTopicArn: { Ref: MyTopic }
It is important that the mapping template will return a valid
application/x-www-form-urlencoded
string
Source: Connect AWS API Gateway directly to SNS using a service integration
You can customize the response body by providing mapping templates for success, server errors (5xx) and client errors (4xx).
Templates must be in JSON format. If a template isn't provided, the integration response will be returned as-is to the client.
custom:
apiGatewayServiceProxies:
- kinesis:
path: /kinesis
method: post
streamName: { Ref: 'MyStream' }
response:
template:
success: |
{
"success": true
}
serverError: |
{
"success": false,
"errorMessage": "Server Error"
}
clientError: |
{
"success": false,
"errorMessage": "Client Error"
}
Author: Serverless-operations
Source Code: https://github.com/serverless-operations/serverless-apigateway-service-proxy
License:
1593235440
Data Science, Data Analytics, Big Data, these are the buzz words of today’s world. A huge amount of data is being generated and analyzed every day. So communicating the insights from that data becomes crucial. Charts help visualize the data and communicate the result of the analysis with charts, it becomes easy to understand the data.
There are a lot of libraries for angular that can be used to build charts. In this blog, we will look at one such library, NGX-Charts. We will see how to use it in angular and how to build data visualizations.
What we will cover:
Installing ngx-chart.
Building a vertical bar graph.
Building a pie chart.
Building an advanced pie chart.
NGX-Chart charting framework for angular2+. It’s open-source and maintained by Swimlane.
NGX-Charts does not merely wrap d3, nor any other chart engine for that matter. It is using Angular to render and animate the SVG elements with all of its binding and speed goodness and uses d3 for the excellent math functions, scales, axis and shape generators, etc. By having Angular do all of the renderings it opens us up to endless possibilities the Angular platform provides such as AoT, Universal, etc.
NGX-Charts supports various chart types like bar charts, line charts, area charts, pie charts, bubble charts, doughnut charts, gauge charts, heatmap, treemap, and number cards.
1. Install the ngx-chart package in your angular app.
npm install @swimlane/ngx-charts --save
2. At the time of installing or when you serve your application is you get an error:
ERROR in The target entry-point "@swimlane/ngx-charts" has missing dependencies: - @angular/cdk/portal
You also need to install angular/cdk
npm install @angular/cdk --save
3. Import NgxChartsModule from ‘ngx-charts’ in AppModule
4. NgxChartModule also requires BrowserAnimationModule. Import is inAppModule.
app.module.ts
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
import { NgxChartsModule }from '@swimlane/ngx-charts';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
BrowserAnimationsModule,
NgxChartsModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
Amazing! Now we can start using ngx-chart component and build the graph we want.
In the AppComponent we will provide data that the chart will represent. It’s a sample data for vehicles on the road survey.
#angular #angular 6 #scala #angular #angular 9 #bar chart #charting #charts #d3 charts #data visualisation #ngx #ngx charts #pie
1600376640
![]() |
![]() |
---|
The library was designed to create aesthetic, animated (so far only linear) charts based on a given input. The main assumptions of the library were to create smooth transitions between subsequent data sets. For this reason, we have discovered a shortage of existing libraries related to the charts. The current package was created as part of the Rainbow.me project and for this reason it was not designed as a complete and comprehensive solution for displaying various types of charts. However, we will be now using more charts in the whole application, so we believe that the number of functionalities in the application will gradually grow.
Additionally, we are open to new Pull Requests. We want this library to become popular and complete thanks to community activity.
It’s a part of the Rainbow.me project.
The library has been released in a production-ready version. We use it inside the Rainbow.me project so it’s verified for use in production. However, it relies on React Native Reanimated 2.0 in the alpha version thus it might not work perfectly. Test it deeply before using it. Until the stable release of Reanimated, I think it’s worth not marking this library as stable. Although the library works with Reanimated without any changes, we faced a few issues related to our (quite advanced) usage of the library. Thus we made some hacks we’re not very proud of and it’s for 99% something you should not do. However, if you see some crashes, you may try one of our hacks.
There’re a few things left to make it polished regarding linear charts:
ChartProvider
and ChartPath
have been split for two components to separated responsibilities of providing data and displaying charts. I’m still not sure if it’s a good move so we can decide to move some props from one to another or connect them inside one component.springConfig
and timingConfig
)yarn add @rainbow-me/animated-charts
npm i @rainbow-me/animated-charts
yarn add react-native-haptic-feedback
npm i react-native-haptic-feedback
The library is verified on 2.0.0-alpha.6
version of reanimated.
Using TurboModules might have an impact on your current development flow and most likely you don’t want to decrease your DX. Since we’re not using reanimated in other places in the app, we made some tweaks to disable charts in development mode with compilation macros on iOS. You can find it here
Also, because we’re using libraries which currently do not support reanimated 2, we patched exports in reanimated
Furthermore, we found few differences in how the Animated
module works with and without TurboModules support, so we made a trick to fallback to the not-TM version of Animated.
Most likely, you don’t need any of those patches.
We made a generic example to show briefly what’s possible to achieve with this library. A Real-life example is available inside Rainbow!
In order to run an example clone this repo and navigate to Example
then:
yarn && cd ios && pod install && cd ..
react-native run-android
react-native run-ios
The library has been designed to provide as much flexibility as possible with the component-based API for easy integration with existing applications.
import React from 'react';
import {Dimensions, View} from 'react-native';
import {ChartDot, ChartPath, ChartPathProvider, monotoneCubicInterpolation} from '@rainbow-me/animated-charts';
export const {width: SIZE} = Dimensions.get('window');
export const data = [
{x: 1453075200, y: 1.47}, {x: 1453161600, y: 1.37},
{x: 1453248000, y: 1.53}, {x: 1453334400, y: 1.54},
{x: 1453420800, y: 1.52}, {x: 1453507200, y: 2.03},
{x: 1453593600, y: 2.10}, {x: 1453680000, y: 2.50},
{x: 1453766400, y: 2.30}, {x: 1453852800, y: 2.42},
{x: 1453939200, y: 2.55}, {x: 1454025600, y: 2.41},
{x: 1454112000, y: 2.43}, {x: 1454198400, y: 2.20},
];
const points = monotoneCubicInterpolation(data)(40);
const BasicExample = () => (
<View style={{ backgroundColor: 'black' }}>
<ChartPathProvider data={{ points, smoothingStrategy: 'bezier' }}>
<ChartPath height={SIZE / 2} stroke="yellow" width={SIZE} />
<ChartDot style={{ backgroundColor: 'blue' }} />
</ChartPathProvider>
</View>
);
The code above generates the chart below:
The whole chart’s structure has to be wrapped with ChartProvider
. It’s responsible for data managing and itself does not have a visual impact on the layout. Under the hood, it uses context API to simplify manipulation with other components. The rule is to use one data series for each wrapper.
Prop name | type | default / obligatory | description |
---|---|---|---|
softMargin |
number |
0 |
While scrubbing the chart touching edges of the screen you may want make points on the edges more accessible. With softMargin it’s possible to access points on edges doubling the speed of scrubbing beyond this margin. |
enableHaptics |
boolean |
false |
On pressing in/out on the chart it might be expected to make haptic feedback. It will happen with enableHaptics set to true and react-native-haptic-feedback installed |
data |
`{ points: [Point], nativePoints: [Point], smoothingStrategy?: ‘bezier’ | ‘simple’ | ‘complex’, smoothingFactor }` |
springConfig |
object | {damping: 15, mass: 1, stiffness: 600} |
Object defining the spring animation. This spring is used for a dot’s scale. |
timingFeedbackConfig |
object | {duration: 80} |
Object defining the timing animation. timingFeedbackConfig is used for a path’s opacity and width. |
timingAnimationConfig |
object | {duration: 300} |
Object defining the timing animation. timingAnimationConfig is used for the transition between chart’s data. |
data
is an array containing points to be displayed. A Point
is an object containing x
and y
as a number.nativeData
is an array of points that will not be drawn. However, if you used some strategy of interpolating data or simplifying you might want to present data slightly different from the real one. Then if you’d like labels to be fully correct you may want to provide real data before adjusting them.smoothingStrategy
. While presenting points path can be drawn with different approaches.
smoothingStrategy
is not provided (or set to any other value but for listed here), connects points using linear interpolation.bezier
strategy connects points with a bezier path inspired by d3 shape. It’s not parametrized by smoothingFactor
.complex
strategy uses approach explained here using cubic splines. It’s parametrized by smoothingFactor
.simple
strategy is a bit simplified complex
strategy using quadratic splines. It’s parametrized by smoothingFactor
.smoothingFactor
. Is a value from 0
to 1
defining how smooth a presentation should be. 0
means no smoothing, and it’s the default. smoothingFactor
has an impact if smoothingStrategy
is simple
or complex
.This component is used for showing the path itself.
Prop name | type | default / obligatory | description |
---|---|---|---|
disableSmoothingWhileTransitioning |
number |
false |
Although smoothing is not complex computing, it might impact performance in some low-end devices so while having a big set of data it might be worth disable smoothing while transitioning. |
height |
number |
obligatory | Height od the SVG canvas |
width |
number |
obligatory | Width od the SVG canvas |
strokeWidth |
number |
1 |
Width of the path. |
strokeWidthSelected |
number |
1 |
Width of the path selected. |
gestureEnabled |
boolean |
true |
Defines if interaction with the chart should be allowed or not |
longPressGestureHandlerProps |
object |
{maxDist: 100000, minDurationMs: 0, shouldCancelWhenOutside: false} |
Under the hood we’re using LongPressGestureHandler for handling interactions. It’s recommended to not override its props. However, it might be useful while interacting with another GH. |
selectedOpacity |
number |
0.7 |
Target opacity of the path while touching the chart. |
…rest | object |
{} |
Props applied to SVG Path. |
Component for displaying the dot for scrubbing on the chart.
Prop name | type | default | description |
---|---|---|---|
size |
number |
10 |
Size of the dot. |
…props | object |
{} |
Rest of the props applied to Reanimated.View including style |
&
ChartXLabelLabels are useful while moving finger through the chart to show the exact value in given point.
Prop name | type | default | description |
---|---|---|---|
format |
reanimated worklet | a => a |
Worklet for formatting data from the chart. It can be useful when your data is a timestamp or currency. |
…props | object |
{} |
Rest of the props applied to TextInput including style |
TODO
TODO
It’s not essential in the library, but we have decided to include a lot of helpers we are (or we were) using for displaying charts.
We have two interpolators which share the most of the API: bSplineInterpolation
and monotoneCubicInterpolation
.
import { bSplineInterpolation as interpolator } from '@rainbow-me/animated-charts';
// import { monotoneCubicInterpolation as interpolator } from '@rainbow-me/animated-charts';
const interpolatedData = interpolator(data)(80, true, false);
Code above generates 80 equidistant points from given dataset. Interpolator (monotoneCubicInterpolation
or bSplineInterpolation
) returns generator. Generator accepts 3 arguments: range
, includeExtremes
, removePointsSurroundingExtremes
.
range
is the number of points of the output.
includeExtremes
. If it’s vital to include extremes in the output, set to true. However, the data might not be fully equidistant.
removePointsSurroundingExtremes
. Makes sense only if includeExtremes
set to true
. When disabled, it might be possible that extremes look very “pointy”. To get rid of this, you can remove points surrounding extremes. E.g.
removePointsSurroundingExtremes = false
o---------o----Min--o---------o---------o---------o---------o
removePointsSurroundingExtremes = true
o--------------Min------------o---------o---------o---------o
bSplineInterpolation(data, degree = 3)
bSplineInterpolation
is inspired by victorian lib and uses B-spline interpolation of a given degree
.
monotoneCubicInterpolation(data, degree = 3)
This curve is inspired by d3 shape. "Produces a cubic spline that preserves monotonicity in y, assuming monotonicity in x, as proposed by Steffen in A simple method for monotonic interpolation in one dimension: “a smooth curve with continuous first-order derivatives that passes through any given set of data points without spurious oscillations. Local extrema can occur only at grid points where they are given by the data, but not in between two adjacent grid points.”
simplifyData(data, pickRange = 10, includeExtremes = true)
This helper takes only one point per pickRange
. Might be useful for very dense data. If it’s important, it’s possible to include extremes with the includeExtremes
flag. E.g.
pickRange = 3, includeExtremes = true
X are equidistant in this case
Y:0 1 7 2 -3 0 1 2
S----------o----------E----------X----------E----------o----------X----------o----------S
X
- points picked because index%3=0
S
– the first and the last points are always included.
E
– extremes.
Author: rainbow-me
Source Code: https://github.com/rainbow-me/react-native-animated-charts
#react-native #react #mobile-apps