Include ui-* packages in repo (development speed) (#65)
* Include ui-* packages in repo (development speed) * Bump * Bump * Cleanup dependencies
This commit is contained in:
parent
4d1156bbfb
commit
ba36eede0b
@ -15,4 +15,4 @@ all=warn
|
||||
include_warnings=true
|
||||
module.name_mapper='^@polkadot/app-\(accounts\|addresses\|explorer\|extrinsics\|home\|rpc\|settings\|storage\|toolbox\|vanitygen\)\(.*\)$' -> '<PROJECT_ROOT>/packages/app-\1/src\2'
|
||||
module.name_mapper='^@polkadot/apps\(.*\)$' -> '<PROJECT_ROOT>/packages/apps/src\1'
|
||||
module.name_mapper='^@polkadot/ui-\(app\|keyring\|signer\)\(.*\)$' -> '<PROJECT_ROOT>/packages/ui-\1/src\2'
|
||||
module.name_mapper='^@polkadot/ui-\(app\|identicon\|keyring\|react-rx\|react\|signer\)\(.*\)$' -> '<PROJECT_ROOT>/packages/ui-\1/src\2'
|
||||
|
||||
18
README.md
18
README.md
@ -29,5 +29,21 @@ The repo is split into a number of packages, each representing an application. T
|
||||
|
||||
In addition the following libraries are also included in the repo. These are to be moved to the [@polkadot/ui](https://github.com/polkadot-js/ui/) repository once it reaches a base level of stability and usability. (At this point with the framework being tested on the apps above, it makes development easier having it close)
|
||||
|
||||
- [ui-keyring](packages/ui-keyring/) A browser-specific wrapper around the base [@polkadot/util-keyring](https://github.com/polkadot-js/util/) libary.
|
||||
- [ui-app](packages/ui-app/) A reactive (using RxJS) application framework with a number of useful shared components.
|
||||
- [ui-keyring](packages/ui-keyring/) A browser-specific wrapper around the base [@polkadot/util-keyring](https://github.com/polkadot-js/util/) library.
|
||||
- [ui-signer](packages/ui-signer/) Signer implementation for apps.
|
||||
- [packages/ui-identicon](packages/ui-identicon/) Identity icon generator with raw publicKey as input
|
||||
- [packages/ui-react](packages/ui-react) A collection of base React components
|
||||
- [packages/ui-react-rx](packages/ui-react-rx) Base components that use the RxJS Observable APIs
|
||||
|
||||
## Application
|
||||
|
||||
For development purposes, the apps entry point can be launched with `yarn run start` and accessing the application of [http://localhost:3000](http://localhost:3000)
|
||||
|
||||
## Demos
|
||||
|
||||
Demos for the different libraries can be viewed with `yarn run demo:<name>` and then browsing to [http://localhost:3000](http://localhost:3000). Available demo -
|
||||
|
||||
- `yarn run demo:identicon`
|
||||
- `yarn run demo:ui`
|
||||
- `yarn run demo:rx`
|
||||
|
||||
8
flow-typed/semver.js
vendored
Normal file
8
flow-typed/semver.js
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
// @flow
|
||||
|
||||
declare module 'semver' {
|
||||
declare module.exports: {
|
||||
gt: (a: string, b: string) => boolean,
|
||||
lt: (a: string, b: string) => boolean
|
||||
}
|
||||
}
|
||||
@ -1 +1,10 @@
|
||||
module.exports = require('@polkadot/dev-react/config/jest');
|
||||
const config = require('@polkadot/dev-react/config/jest');
|
||||
|
||||
module.exports = Object.assign({}, config, {
|
||||
moduleNameMapper: {
|
||||
'@polkadot/app-(accounts|addresses|explorer|extrinsics|rpc|storage|toolbox|vanitygen)(.*)$': '<rootDir>/packages/ui-$1/src/$2',
|
||||
'@polkadot/ui-(app|identicon|keyring|react-rx|react|signer)(.*)$': '<rootDir>/packages/ui-$1/src/$2',
|
||||
'\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$': 'empty/object',
|
||||
'\\.(css|less)$': 'empty/object'
|
||||
}
|
||||
});
|
||||
|
||||
@ -10,5 +10,5 @@
|
||||
"packages": [
|
||||
"packages/*"
|
||||
],
|
||||
"version": "0.7.2"
|
||||
"version": "0.17.0"
|
||||
}
|
||||
|
||||
@ -9,10 +9,13 @@
|
||||
"build": "polkadot-dev-build-babel",
|
||||
"check": "eslint packages && flow check",
|
||||
"clean": "polkadot-dev-clean-build",
|
||||
"demo:identicon": "webpack-serve --config packages/ui-identicon/webpack.config.js --content packages/ui-identicon --port 3000",
|
||||
"demo:rx": "webpack-serve --config packages/ui-react-rx/webpack.config.js --content packages/ui-react-rx --port 3000",
|
||||
"demo:ui": "webpack-serve --config packages/ui-react/webpack.config.js --content packages/ui-react --port 3000",
|
||||
"deploy:ghpages": "gh-pages -d packages/apps/build",
|
||||
"vanitygen": "polkadot-dev-build-babel && node packages/app-vanitygen/build/generator/cli.js",
|
||||
"start": "cd packages/apps && webpack-serve --config webpack.config.js --port 3000",
|
||||
"test": "echo 'no tests executed'"
|
||||
"test": "jest --coverage"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@polkadot/dev-react": "^0.19.11",
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@polkadot/app-accounts",
|
||||
"version": "0.7.2",
|
||||
"version": "0.17.0",
|
||||
"main": "index.js",
|
||||
"repository": "https://github.com/polkadot-js/apps.git",
|
||||
"author": "Jaco Greeff <jacogr@gmail.com>",
|
||||
@ -12,6 +12,6 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.0.0-beta.47",
|
||||
"@polkadot/ui-app": "^0.7.2"
|
||||
"@polkadot/ui-app": "^0.17.0"
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@polkadot/app-addresses",
|
||||
"version": "0.7.2",
|
||||
"version": "0.17.0",
|
||||
"main": "index.js",
|
||||
"repository": "https://github.com/polkadot-js/apps.git",
|
||||
"author": "Jaco Greeff <jacogr@gmail.com>",
|
||||
@ -12,7 +12,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.0.0-beta.47",
|
||||
"@polkadot/app-accounts": "^0.7.2",
|
||||
"@polkadot/ui-app": "^0.7.2"
|
||||
"@polkadot/app-accounts": "^0.17.0",
|
||||
"@polkadot/ui-app": "^0.17.0"
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@polkadot/app-explorer",
|
||||
"version": "0.7.2",
|
||||
"version": "0.17.0",
|
||||
"main": "index.js",
|
||||
"repository": "https://github.com/polkadot-js/apps.git",
|
||||
"author": "Jaco Greeff <jacogr@gmail.com>",
|
||||
@ -14,7 +14,7 @@
|
||||
"@babel/runtime": "^7.0.0-beta.47",
|
||||
"@polkadot/primitives-codec": "^0.17.2",
|
||||
"@polkadot/primitives-json": "^0.17.2",
|
||||
"@polkadot/ui-app": "^0.7.2",
|
||||
"@polkadot/ui-app": "^0.17.0",
|
||||
"@polkadot/util-crypto": "^0.22.2"
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@polkadot/app-extrinsics",
|
||||
"version": "0.7.2",
|
||||
"version": "0.17.0",
|
||||
"main": "index.js",
|
||||
"repository": "https://github.com/polkadot-js/apps.git",
|
||||
"author": "Jaco Greeff <jacogr@gmail.com>",
|
||||
@ -13,8 +13,8 @@
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.0.0-beta.47",
|
||||
"@polkadot/extrinsics-codec": "^0.17.2",
|
||||
"@polkadot/ui-app": "^0.7.2",
|
||||
"@polkadot/ui-signer": "^0.7.2",
|
||||
"@polkadot/ui-app": "^0.17.0",
|
||||
"@polkadot/ui-signer": "^0.17.0",
|
||||
"react-dropzone": "^4.2.9"
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@polkadot/app-rpc",
|
||||
"version": "0.7.2",
|
||||
"version": "0.17.0",
|
||||
"main": "index.js",
|
||||
"repository": "https://github.com/polkadot-js/apps.git",
|
||||
"author": "Jaco Greeff <jacogr@gmail.com>",
|
||||
@ -12,8 +12,8 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.0.0-beta.47",
|
||||
"@polkadot/ui-app": "^0.7.2",
|
||||
"@polkadot/ui-keyring": "^0.7.2",
|
||||
"@polkadot/ui-signer": "^0.7.2"
|
||||
"@polkadot/ui-app": "^0.17.0",
|
||||
"@polkadot/ui-keyring": "^0.17.0",
|
||||
"@polkadot/ui-signer": "^0.17.0"
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@polkadot/app-storage",
|
||||
"version": "0.7.2",
|
||||
"version": "0.17.0",
|
||||
"main": "index.js",
|
||||
"repository": "https://github.com/polkadot-js/apps.git",
|
||||
"author": "Jaco Greeff <jacogr@gmail.com>",
|
||||
@ -12,6 +12,6 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.0.0-beta.47",
|
||||
"@polkadot/ui-app": "^0.7.2"
|
||||
"@polkadot/ui-app": "^0.17.0"
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@polkadot/app-toolbox",
|
||||
"version": "0.7.2",
|
||||
"version": "0.17.0",
|
||||
"main": "index.js",
|
||||
"repository": "https://github.com/polkadot-js/apps.git",
|
||||
"author": "Jaco Greeff <jacogr@gmail.com>",
|
||||
@ -12,7 +12,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.0.0-beta.47",
|
||||
"@polkadot/ui-app": "^0.7.2",
|
||||
"@polkadot/ui-keyring": "^0.7.2"
|
||||
"@polkadot/ui-app": "^0.17.0",
|
||||
"@polkadot/ui-keyring": "^0.17.0"
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@polkadot/app-vanitygen",
|
||||
"version": "0.7.2",
|
||||
"version": "0.17.0",
|
||||
"main": "index.js",
|
||||
"repository": "https://github.com/polkadot-js/apps.git",
|
||||
"author": "Jaco Greeff <jacogr@gmail.com>",
|
||||
@ -12,7 +12,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.0.0-beta.47",
|
||||
"@polkadot/ui-app": "^0.7.2",
|
||||
"@polkadot/ui-app": "^0.17.0",
|
||||
"chalk": "^2.4.1",
|
||||
"yargs": "10.1.2"
|
||||
},
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@polkadot/apps",
|
||||
"version": "0.7.2",
|
||||
"version": "0.17.0",
|
||||
"description": "An Apps portal into the Polkadot network",
|
||||
"main": "index.js",
|
||||
"homepage": ".",
|
||||
@ -13,16 +13,16 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.0.0-beta.47",
|
||||
"@polkadot/app-accounts": "^0.7.2",
|
||||
"@polkadot/app-addresses": "^0.7.2",
|
||||
"@polkadot/app-explorer": "^0.7.2",
|
||||
"@polkadot/app-extrinsics": "^0.7.2",
|
||||
"@polkadot/app-rpc": "^0.7.2",
|
||||
"@polkadot/app-storage": "^0.7.2",
|
||||
"@polkadot/app-toolbox": "^0.7.2",
|
||||
"@polkadot/app-vanitygen": "^0.7.2",
|
||||
"@polkadot/ui-app": "^0.7.2",
|
||||
"@polkadot/ui-signer": "^0.7.2"
|
||||
"@polkadot/app-accounts": "^0.17.0",
|
||||
"@polkadot/app-addresses": "^0.17.0",
|
||||
"@polkadot/app-explorer": "^0.17.0",
|
||||
"@polkadot/app-extrinsics": "^0.17.0",
|
||||
"@polkadot/app-rpc": "^0.17.0",
|
||||
"@polkadot/app-storage": "^0.17.0",
|
||||
"@polkadot/app-toolbox": "^0.17.0",
|
||||
"@polkadot/app-vanitygen": "^0.17.0",
|
||||
"@polkadot/ui-app": "^0.17.0",
|
||||
"@polkadot/ui-signer": "^0.17.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-loader": "^8.0.0-beta.2",
|
||||
|
||||
@ -1,17 +0,0 @@
|
||||
// Copyright 2017-2018 Jaco Greeff
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
// @flow
|
||||
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import App from './index';
|
||||
|
||||
describe('App', () => {
|
||||
it('renders without crashing', () => {
|
||||
const div = document.createElement('div');
|
||||
|
||||
ReactDOM.render(<App />, div);
|
||||
ReactDOM.unmountComponentAtNode(div);
|
||||
});
|
||||
});
|
||||
@ -10,7 +10,7 @@ const CopyWebpackPlugin = require('copy-webpack-plugin');
|
||||
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
||||
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||
|
||||
const packages = ['app-accounts', 'app-addresses', 'app-explorer', 'app-extrinsics', 'app-rpc', 'app-storage', 'app-toolbox', 'app-vanitygen', 'ui-app', 'ui-keyring', 'ui-signer'];
|
||||
const packages = ['app-accounts', 'app-addresses', 'app-explorer', 'app-extrinsics', 'app-rpc', 'app-storage', 'app-toolbox', 'app-vanitygen', 'ui-app', 'ui-identicon', 'ui-keyring', 'ui-react-rx', 'ui-react', 'ui-signer'];
|
||||
|
||||
function createWebpack ({ alias = {}, context, name = 'index' }) {
|
||||
const pkgJson = require(path.join(context, 'package.json'));
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@polkadot/ui-app",
|
||||
"version": "0.7.2",
|
||||
"version": "0.17.0",
|
||||
"main": "index.js",
|
||||
"repository": "https://github.com/polkadot-js/apps.git",
|
||||
"author": "Jaco Greeff <jacogr@gmail.com>",
|
||||
@ -14,9 +14,9 @@
|
||||
"@babel/runtime": "^7.0.0-beta.47",
|
||||
"@polkadot/extrinsics": "^0.17.2",
|
||||
"@polkadot/storage": "^0.17.2",
|
||||
"@polkadot/ui-keyring": "^0.7.2",
|
||||
"@polkadot/ui-react": "^0.14.3",
|
||||
"@polkadot/ui-react-rx": "^0.14.3",
|
||||
"@polkadot/ui-keyring": "^0.17.0",
|
||||
"@polkadot/ui-react": "^0.17.0",
|
||||
"@polkadot/ui-react-rx": "^0.17.0",
|
||||
"i18next": "^11.1.1",
|
||||
"i18next-browser-languagedetector": "^2.2.0",
|
||||
"i18next-xhr-backend": "^1.5.1",
|
||||
|
||||
16
packages/ui-identicon/LICENSE
Normal file
16
packages/ui-identicon/LICENSE
Normal file
@ -0,0 +1,16 @@
|
||||
ISC License (ISC)
|
||||
|
||||
Copyright 2016 Dan Finlay
|
||||
Copyright 2017-2018 Jaco Greeff <jacogr@gmail.com>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
PERFORMANCE OF THIS SOFTWARE.
|
||||
39
packages/ui-identicon/README.md
Normal file
39
packages/ui-identicon/README.md
Normal file
@ -0,0 +1,39 @@
|
||||
[](https://polkadot.js.org)
|
||||

|
||||
[](https://github.com/Flet/semistandard)
|
||||
[](https://www.npmjs.com/package/@polkadot/ui-identicon)
|
||||
[](https://travis-ci.org/polkadot-js/ui)
|
||||
[](https://codeclimate.com/github/polkadot-js/apps/maintainability)
|
||||
[](https://coveralls.io/github/polkadot-js/apps?branch=master)
|
||||
[](https://david-dm.org/polkadot-js/apps?path=packages/ui-identicon)
|
||||
[](https://david-dm.org/polkadot-js/apps?path=packages/ui-identicon#info=devDependencies)
|
||||
|
||||
# @polkadot/ui-identicon
|
||||
|
||||
Adapted from [Jazzicon](https://github.com/danfinlay/jazzicon) by Dan Finlay with the following changes -
|
||||
|
||||
- Random values now is read from the Uint8Array supplied (as opposed to having the seed as a number). This allows us to give an publicKey/address as an input and use those values in the pattern generation.
|
||||
- Upgrade to the underlying [color](https://github.com/Qix-/color) library
|
||||
- Generate circles as shapes (instead of rectangles)
|
||||
- Interface updated to take in optional className & style
|
||||
- Update everywhere to use ES6
|
||||
- Split source into self-contained functions (TODO: future testing)
|
||||
- Everything has been updated to use flow
|
||||
- Test the library functions
|
||||
- Copyright headers added (original also under ISC)
|
||||
|
||||
## Usage
|
||||
|
||||
```js
|
||||
import identicon from '@polkadot/ui-identicon';
|
||||
|
||||
const publicKey = new Uint8Array([...]); // 32 bytes
|
||||
const body = document.querySelector('body');
|
||||
const identity = identicon(publicKey, 100 /* diameter, optional */, 'my-class' /* className, optional */ /* style. optional */ /* colors, optional */);
|
||||
|
||||
body.append(identity);
|
||||
```
|
||||
|
||||
Also see [src/demo.js](src/demo.js) for a randomly generated example.
|
||||
|
||||

|
||||
BIN
packages/ui-identicon/demo.png
Normal file
BIN
packages/ui-identicon/demo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 851 KiB |
17
packages/ui-identicon/index.html
Normal file
17
packages/ui-identicon/index.html
Normal file
@ -0,0 +1,17 @@
|
||||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/normalize/5.0.0/normalize.min.css" />
|
||||
<style>
|
||||
#demo {
|
||||
padding: 0.5em;
|
||||
}
|
||||
.padded {
|
||||
margin: 0.5em !important;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="demo"></div>
|
||||
<script src="./demo.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
21
packages/ui-identicon/package.json
Normal file
21
packages/ui-identicon/package.json
Normal file
@ -0,0 +1,21 @@
|
||||
{
|
||||
"name": "@polkadot/ui-identicon",
|
||||
"version": "0.17.0",
|
||||
"description": "Renders an SVG picture representing an address",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"build": "polkadot-dev-build-babel",
|
||||
"check": "eslint src && flow check",
|
||||
"test": "echo \"Tests only available from root wrapper\""
|
||||
},
|
||||
"author": "Jaco Greeff <jacogr@gmail.com>",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.0.0-beta.47",
|
||||
"color": "^3.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@polkadot/util-crypto": "^0.22.2",
|
||||
"xmlserializer": "^0.6.1"
|
||||
}
|
||||
}
|
||||
30
packages/ui-identicon/src/colors.js
Normal file
30
packages/ui-identicon/src/colors.js
Normal file
@ -0,0 +1,30 @@
|
||||
// Copyright 2016 Dan Finlay
|
||||
// Copyright 2017-2018 Jaco Greeff
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
// @flow
|
||||
|
||||
import type { Seeder } from './types';
|
||||
|
||||
type ColorGen = {
|
||||
(alpha?: number): string
|
||||
};
|
||||
|
||||
const Color = require('color');
|
||||
|
||||
const { COLORS } = require('./defaults');
|
||||
|
||||
const WOBBLE = 30;
|
||||
|
||||
module.exports = function colors (seeder: Seeder): ColorGen {
|
||||
const amount = (seeder() * WOBBLE) - (WOBBLE / 2);
|
||||
const all = COLORS.map((hex) => Color(hex).rotate(amount));
|
||||
|
||||
return (alpha: number = 0.9): string => {
|
||||
const index = Math.floor(all.length * seeder());
|
||||
|
||||
return all.splice(index, 1)[0]
|
||||
.alpha(alpha)
|
||||
.string();
|
||||
};
|
||||
};
|
||||
34
packages/ui-identicon/src/colors.spec.js
Normal file
34
packages/ui-identicon/src/colors.spec.js
Normal file
@ -0,0 +1,34 @@
|
||||
// Copyright 2017-2018 Jaco Greeff
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
|
||||
const newSeeder = require('./seeder');
|
||||
const newColors = require('./colors');
|
||||
|
||||
describe('colors', () => {
|
||||
let colors;
|
||||
|
||||
beforeEach(() => {
|
||||
colors = newColors(newSeeder());
|
||||
});
|
||||
|
||||
it('generates using default alpha', () => {
|
||||
expect(
|
||||
colors()
|
||||
).toEqual('hsla(166.70000000000005, 98.6%, 27.6%, 0.9)');
|
||||
});
|
||||
|
||||
it('applies specified alpha', () => {
|
||||
expect(
|
||||
colors(0.5)
|
||||
).toEqual('hsla(166.70000000000005, 98.6%, 27.6%, 0.5)');
|
||||
});
|
||||
|
||||
it('rolates colors', () => {
|
||||
colors();
|
||||
|
||||
expect(
|
||||
colors()
|
||||
).not.toEqual('hsla(166.70000000000005, 98.6%, 27.6%, 0.9)');
|
||||
});
|
||||
});
|
||||
30
packages/ui-identicon/src/container.js
Normal file
30
packages/ui-identicon/src/container.js
Normal file
@ -0,0 +1,30 @@
|
||||
// Copyright 2016 Dan Finlay
|
||||
// Copyright 2017-2018 Jaco Greeff
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
// @flow
|
||||
|
||||
module.exports = function container (diameter: number, background: string = 'white', className: string = '', _style: { [string]: string } = {}): HTMLElement {
|
||||
const element = document.createElement('div');
|
||||
const style = Object.assign({
|
||||
background,
|
||||
borderRadius: `${diameter / 2}px`,
|
||||
display: 'inline-block',
|
||||
height: `${diameter}px`,
|
||||
margin: '0px',
|
||||
overflow: 'hidden',
|
||||
padding: '0px',
|
||||
width: `${diameter}px`
|
||||
}, _style);
|
||||
|
||||
element.className = className;
|
||||
element.style.background = background;
|
||||
|
||||
// FIXME Flow seems to believe element.style[key] has number keys
|
||||
// flowlint-next-line unclear-type:off
|
||||
Object.keys(style).forEach((key: any) => {
|
||||
element.style[key] = style[key];
|
||||
});
|
||||
|
||||
return element;
|
||||
};
|
||||
43
packages/ui-identicon/src/container.spec.js
Normal file
43
packages/ui-identicon/src/container.spec.js
Normal file
@ -0,0 +1,43 @@
|
||||
// Copyright 2017-2018 Jaco Greeff
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
|
||||
const container = require('./container');
|
||||
|
||||
describe('container', () => {
|
||||
it('applies default styles', () => {
|
||||
expect(
|
||||
container(100).style._values
|
||||
).toMatchObject({
|
||||
'background': 'white',
|
||||
'border-radius': '50px',
|
||||
'display': 'inline-block',
|
||||
'height': '100px',
|
||||
'margin': '0px',
|
||||
'overflow': 'hidden',
|
||||
'padding': '0px',
|
||||
'width': '100px'
|
||||
});
|
||||
});
|
||||
|
||||
it('overrides with supplied styles', () => {
|
||||
expect(
|
||||
container(50, 'black', '', { display: 'block' }).style._values
|
||||
).toMatchObject({
|
||||
'background': 'black',
|
||||
'border-radius': '25px',
|
||||
'display': 'block',
|
||||
'height': '50px',
|
||||
'margin': '0px',
|
||||
'overflow': 'hidden',
|
||||
'padding': '0px',
|
||||
'width': '50px'
|
||||
});
|
||||
});
|
||||
|
||||
it('applies the specified className', () => {
|
||||
expect(
|
||||
container(100, 'blue', 'testClass').className
|
||||
).toEqual('testClass');
|
||||
});
|
||||
});
|
||||
25
packages/ui-identicon/src/defaults.js
Normal file
25
packages/ui-identicon/src/defaults.js
Normal file
@ -0,0 +1,25 @@
|
||||
// Copyright 2016 Dan Finlay
|
||||
// Copyright 2017-2018 Jaco Greeff
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
// @flow
|
||||
|
||||
const COLORS: Array<string> = [
|
||||
'#01888C', // teal
|
||||
'#FC7500', // bright orange
|
||||
'#034F5D', // dark teal
|
||||
'#F73F01', // orangered
|
||||
'#FC1960', // magenta
|
||||
'#C7144C', // raspberry
|
||||
'#F3C100', // goldenrod
|
||||
'#1598F2', // lightning blue
|
||||
'#2465E1', // sail blue
|
||||
'#F19E02' // gold
|
||||
];
|
||||
|
||||
const SHAPE_COUNT = 5;
|
||||
|
||||
module.exports = {
|
||||
COLORS,
|
||||
SHAPE_COUNT
|
||||
};
|
||||
35
packages/ui-identicon/src/demo.js
Normal file
35
packages/ui-identicon/src/demo.js
Normal file
@ -0,0 +1,35 @@
|
||||
// Copyright 2016 Dan Finlay
|
||||
// Copyright 2017-2018 Jaco Greeff
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
// @flow
|
||||
|
||||
const randomAsU8a = require('@polkadot/util-crypto/random/asU8a');
|
||||
|
||||
const identicon = require('./index');
|
||||
|
||||
const element = document.getElementById('demo');
|
||||
|
||||
if (!element) {
|
||||
throw new Error('Unable to find #demo element');
|
||||
}
|
||||
|
||||
function generateIcon (seed: Uint8Array = randomAsU8a(32)): void {
|
||||
const start = Date.now();
|
||||
|
||||
element.append(
|
||||
identicon(seed, 100, 'padded')
|
||||
);
|
||||
|
||||
console.log(`Icon generated in ${(Date.now() - start)}ms`);
|
||||
}
|
||||
|
||||
function generateIcons (count: number = 512): void {
|
||||
generateIcon(new Uint8Array(32));
|
||||
|
||||
for (let index = 1; index < count; index++) {
|
||||
generateIcon();
|
||||
}
|
||||
}
|
||||
|
||||
generateIcons();
|
||||
32
packages/ui-identicon/src/index.js
Normal file
32
packages/ui-identicon/src/index.js
Normal file
@ -0,0 +1,32 @@
|
||||
// Copyright 2016 Dan Finlay
|
||||
// Copyright 2017-2018 Jaco Greeff
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
// @flow
|
||||
|
||||
const colors = require('./colors');
|
||||
const newContainer = require('./container');
|
||||
const newSeeder = require('./seeder');
|
||||
const newShape = require('./shape/circle');
|
||||
const newElement = require('./svg/element');
|
||||
const { SHAPE_COUNT } = require('./defaults');
|
||||
|
||||
module.exports = function identicon (seed: string | Uint8Array, diameter: number = 256, className: string = '', style?: { [string]: string }): HTMLElement {
|
||||
const seeder = newSeeder(seed);
|
||||
const colorGen = colors(seeder);
|
||||
const outer = newContainer(diameter, 'white', className, style);
|
||||
const container = newContainer(diameter, colorGen());
|
||||
const svg = newElement(diameter);
|
||||
|
||||
outer.appendChild(container);
|
||||
container.appendChild(svg);
|
||||
|
||||
for (let count = 0; count < SHAPE_COUNT; count++) {
|
||||
const fill = colorGen();
|
||||
const shape = newShape(seeder, fill, diameter, count);
|
||||
|
||||
svg.appendChild(shape);
|
||||
}
|
||||
|
||||
return outer;
|
||||
};
|
||||
25
packages/ui-identicon/src/index.spec.js
Normal file
25
packages/ui-identicon/src/index.spec.js
Normal file
@ -0,0 +1,25 @@
|
||||
// Copyright 2017-2018 Jaco Greeff
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
|
||||
const xmlserializer = require('xmlserializer');
|
||||
|
||||
const identicon = require('./index');
|
||||
|
||||
describe('identicon', () => {
|
||||
it('generates a basic [0,..,0] identicon', () => {
|
||||
expect(
|
||||
xmlserializer.serializeToString(
|
||||
identicon(new Uint8Array(32))
|
||||
)
|
||||
).toEqual('<div xmlns="http://www.w3.org/1999/xhtml" class="" style="background: white; border-radius: 128px; display: inline-block; height: 256px; margin: 0px; overflow: hidden; padding: 0px; width: 256px;"><div class="" style="border-radius: 128px; display: inline-block; height: 256px; margin: 0px; overflow: hidden; padding: 0px; width: 256px;"><svg xmlns="http://www.w3.org/2000/svg" x="0" y="0" width="256" height="256"><circle cx="128" cy="140.8" r="128" fill="hsla(12.899999999999977, 100%, 49.4%, 0.9)"/><circle cx="128" cy="153.6" r="102.4" fill="hsla(174.29999999999995, 93.7%, 18.8%, 0.9)"/><circle cx="128" cy="166.4" r="76.8" fill="hsla(0.10000000000002274, 99.2%, 48.6%, 0.9)"/><circle cx="128" cy="179.2" r="51.2" fill="hsla(326.20000000000005, 97.4%, 54.3%, 0.9)"/><circle cx="128" cy="192" r="25.6" fill="hsla(326.20000000000005, 81.7%, 42.9%, 0.9)"/></svg></div></div>');
|
||||
});
|
||||
|
||||
it('allows overrides', () => {
|
||||
expect(
|
||||
xmlserializer.serializeToString(
|
||||
identicon(new Uint8Array(32), 100, 'testClass', { display: 'block' })
|
||||
)
|
||||
).toEqual('<div xmlns="http://www.w3.org/1999/xhtml" class="testClass" style="background: white; border-radius: 50px; display: block; height: 100px; margin: 0px; overflow: hidden; padding: 0px; width: 100px;"><div class="" style="border-radius: 50px; display: inline-block; height: 100px; margin: 0px; overflow: hidden; padding: 0px; width: 100px;"><svg xmlns="http://www.w3.org/2000/svg" x="0" y="0" width="100" height="100"><circle cx="50" cy="55" r="50" fill="hsla(12.899999999999977, 100%, 49.4%, 0.9)"/><circle cx="50" cy="60" r="40" fill="hsla(174.29999999999995, 93.7%, 18.8%, 0.9)"/><circle cx="50" cy="65" r="30" fill="hsla(0.10000000000002274, 99.2%, 48.6%, 0.9)"/><circle cx="50" cy="70" r="20" fill="hsla(326.20000000000005, 97.4%, 54.3%, 0.9)"/><circle cx="50" cy="75" r="10" fill="hsla(326.20000000000005, 81.7%, 42.9%, 0.9)"/></svg></div></div>');
|
||||
});
|
||||
});
|
||||
35
packages/ui-identicon/src/seeder.js
Normal file
35
packages/ui-identicon/src/seeder.js
Normal file
@ -0,0 +1,35 @@
|
||||
// Copyright 2017-2018 Jaco Greeff
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
// @flow
|
||||
|
||||
import type { Seeder } from './types';
|
||||
|
||||
const isU8a = require('@polkadot/util/is/u8a');
|
||||
const u8aFromString = require('@polkadot/util/u8a/fromString');
|
||||
|
||||
const DIVISOR = 256 * 256;
|
||||
|
||||
module.exports = function seeder (_seed: string | Uint8Array = new Uint8Array(32)): Seeder {
|
||||
// $FlowFixMe type checking
|
||||
const seed: Uint8Array = isU8a(_seed)
|
||||
? _seed
|
||||
// $FlowFixMe type has been determined
|
||||
: u8aFromString(_seed);
|
||||
|
||||
let index = (seed[Math.floor(seed.length / 2)] % seed.length) - 1;
|
||||
|
||||
const next = () => {
|
||||
index += 1;
|
||||
|
||||
if (index === seed.length) {
|
||||
index = 0;
|
||||
}
|
||||
|
||||
return seed[index];
|
||||
};
|
||||
|
||||
return (): number => {
|
||||
return ((next() * 256) + next()) / DIVISOR;
|
||||
};
|
||||
};
|
||||
27
packages/ui-identicon/src/seeder.spec.js
Normal file
27
packages/ui-identicon/src/seeder.spec.js
Normal file
@ -0,0 +1,27 @@
|
||||
// Copyright 2017-2018 Jaco Greeff
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
|
||||
const newSeeder = require('./seeder');
|
||||
|
||||
describe('seeder', () => {
|
||||
let seeder;
|
||||
|
||||
beforeEach(() => {
|
||||
seeder = newSeeder(new Uint8Array([1, 2, 3, 4]));
|
||||
});
|
||||
|
||||
it('generates numbers using 2 spaces', () => {
|
||||
expect(
|
||||
seeder()
|
||||
).toEqual(0.0156402587890625);
|
||||
});
|
||||
|
||||
it('generates numbers using 2 spaces (incremented)', () => {
|
||||
seeder();
|
||||
|
||||
expect(
|
||||
seeder()
|
||||
).toEqual(0.0078582763671875);
|
||||
});
|
||||
});
|
||||
24
packages/ui-identicon/src/shape/circle.js
Normal file
24
packages/ui-identicon/src/shape/circle.js
Normal file
@ -0,0 +1,24 @@
|
||||
// Copyright 2016 Dan Finlay
|
||||
// Copyright 2017-2018 Jaco Greeff
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
// @flow
|
||||
|
||||
import type { Seeder } from '../types';
|
||||
|
||||
const newCircle = require('../svg/circle');
|
||||
const { SHAPE_COUNT } = require('../defaults');
|
||||
|
||||
module.exports = function circle (seeder: Seeder, fill: string, diameter: number, count: number): Element {
|
||||
const center = diameter / 2;
|
||||
const angle = seeder() * 360;
|
||||
const radius = (((SHAPE_COUNT - count) / SHAPE_COUNT) * (diameter / 2)) + ((diameter / 8) * seeder());
|
||||
const offset = (diameter / 4) * (seeder() + ((count + 1) / SHAPE_COUNT));
|
||||
const cx = (offset * Math.sin(angle)) + center;
|
||||
const cy = (offset * Math.cos(angle)) + center;
|
||||
const svg = newCircle(radius, cx, cy);
|
||||
|
||||
svg.setAttributeNS(null, 'fill', fill);
|
||||
|
||||
return svg;
|
||||
};
|
||||
18
packages/ui-identicon/src/shape/circle.spec.js
Normal file
18
packages/ui-identicon/src/shape/circle.spec.js
Normal file
@ -0,0 +1,18 @@
|
||||
// Copyright 2017-2018 Jaco Greeff
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
|
||||
const xmlserializer = require('xmlserializer');
|
||||
|
||||
const seeder = require('../seeder');
|
||||
const circle = require('./circle');
|
||||
|
||||
describe('circle', () => {
|
||||
it('creates a circle shape', () => {
|
||||
expect(
|
||||
xmlserializer.serializeToString(
|
||||
circle(seeder(), 'blue', 50, 2)
|
||||
)
|
||||
).toEqual('<circle xmlns="http://www.w3.org/2000/svg" cx="25" cy="32.5" r="15" fill="blue"/>');
|
||||
});
|
||||
});
|
||||
27
packages/ui-identicon/src/shape/square.js
Normal file
27
packages/ui-identicon/src/shape/square.js
Normal file
@ -0,0 +1,27 @@
|
||||
// Copyright 2016 Dan Finlay
|
||||
// Copyright 2017-2018 Jaco Greeff
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
// @flow
|
||||
|
||||
import type { Seeder } from '../types';
|
||||
|
||||
const newRect = require('../svg/rect');
|
||||
const { SHAPE_COUNT } = require('../defaults');
|
||||
|
||||
module.exports = function square (seeder: Seeder, fill: string, diameter: number, count: number): Element {
|
||||
const center = diameter / 2;
|
||||
const svg = newRect(diameter);
|
||||
const firstRot = seeder();
|
||||
const angle = Math.PI * 2 * firstRot;
|
||||
const scale = count / SHAPE_COUNT;
|
||||
const velocity = ((diameter / SHAPE_COUNT) * seeder()) + (scale * diameter);
|
||||
const tx = (Math.cos(angle) * velocity).toFixed(3);
|
||||
const ty = (Math.sin(angle) * velocity).toFixed(3);
|
||||
const rot = ((firstRot * 360) + (seeder() * 180)).toFixed(1);
|
||||
|
||||
svg.setAttributeNS(null, 'transform', `translate(${tx} ${ty}) rotate(${rot} ${center} ${center})`);
|
||||
svg.setAttributeNS(null, 'fill', fill);
|
||||
|
||||
return svg;
|
||||
};
|
||||
16
packages/ui-identicon/src/svg/circle.js
Normal file
16
packages/ui-identicon/src/svg/circle.js
Normal file
@ -0,0 +1,16 @@
|
||||
// Copyright 2017-2018 Jaco Greeff
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
// @flow
|
||||
|
||||
const svg = require('./svg');
|
||||
|
||||
module.exports = function circle (r: number, cx: number, cy: number): Element {
|
||||
const elem = svg('circle');
|
||||
|
||||
elem.setAttributeNS(null, 'cx', `${cx}`);
|
||||
elem.setAttributeNS(null, 'cy', `${cy}`);
|
||||
elem.setAttributeNS(null, 'r', `${r}`);
|
||||
|
||||
return elem;
|
||||
};
|
||||
17
packages/ui-identicon/src/svg/circle.spec.js
Normal file
17
packages/ui-identicon/src/svg/circle.spec.js
Normal file
@ -0,0 +1,17 @@
|
||||
// Copyright 2017-2018 Jaco Greeff
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
|
||||
const xmlserializer = require('xmlserializer');
|
||||
|
||||
const circle = require('./circle');
|
||||
|
||||
describe('circle', () => {
|
||||
it('creates a basic SVG circle element', () => {
|
||||
expect(
|
||||
xmlserializer.serializeToString(
|
||||
circle(123, 12, 34)
|
||||
)
|
||||
).toEqual('<circle xmlns="http://www.w3.org/2000/svg" cx="12" cy="34" r="123"/>');
|
||||
});
|
||||
});
|
||||
18
packages/ui-identicon/src/svg/element.js
Normal file
18
packages/ui-identicon/src/svg/element.js
Normal file
@ -0,0 +1,18 @@
|
||||
// Copyright 2016 Dan Finlay
|
||||
// Copyright 2017-2018 Jaco Greeff
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
// @flow
|
||||
|
||||
const svg = require('./svg');
|
||||
|
||||
module.exports = function element (size: number, type: string = 'svg', x: number = 0, y: number = 0): Element {
|
||||
const elem = svg(type);
|
||||
|
||||
elem.setAttributeNS(null, 'x', `${x}`);
|
||||
elem.setAttributeNS(null, 'y', `${y}`);
|
||||
elem.setAttributeNS(null, 'width', `${size}`);
|
||||
elem.setAttributeNS(null, 'height', `${size}`);
|
||||
|
||||
return elem;
|
||||
};
|
||||
17
packages/ui-identicon/src/svg/element.spec.js
Normal file
17
packages/ui-identicon/src/svg/element.spec.js
Normal file
@ -0,0 +1,17 @@
|
||||
// Copyright 2017-2018 Jaco Greeff
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
|
||||
const xmlserializer = require('xmlserializer');
|
||||
|
||||
const element = require('./element');
|
||||
|
||||
describe('element', () => {
|
||||
it('creates a basic SVG element', () => {
|
||||
expect(
|
||||
xmlserializer.serializeToString(
|
||||
element(123)
|
||||
)
|
||||
).toEqual('<svg xmlns="http://www.w3.org/2000/svg" x="0" y="0" width="123" height="123"/>');
|
||||
});
|
||||
});
|
||||
15
packages/ui-identicon/src/svg/rect.js
Normal file
15
packages/ui-identicon/src/svg/rect.js
Normal file
@ -0,0 +1,15 @@
|
||||
// Copyright 2017-2018 Jaco Greeff
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
// @flow
|
||||
|
||||
const element = require('./element');
|
||||
|
||||
module.exports = function rect (size: number): Element {
|
||||
const elem = element(size, 'rect');
|
||||
|
||||
elem.setAttributeNS(null, 'rx', `${size / 16}`);
|
||||
elem.setAttributeNS(null, 'ry', `${size / 16}`);
|
||||
|
||||
return elem;
|
||||
};
|
||||
17
packages/ui-identicon/src/svg/rect.spec.js
Normal file
17
packages/ui-identicon/src/svg/rect.spec.js
Normal file
@ -0,0 +1,17 @@
|
||||
// Copyright 2017-2018 Jaco Greeff
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
|
||||
const xmlserializer = require('xmlserializer');
|
||||
|
||||
const rect = require('./rect');
|
||||
|
||||
describe('rect', () => {
|
||||
it('creates a basic SVG rect element', () => {
|
||||
expect(
|
||||
xmlserializer.serializeToString(
|
||||
rect(123)
|
||||
)
|
||||
).toEqual('<rect xmlns="http://www.w3.org/2000/svg" x="0" y="0" width="123" height="123" rx="7.6875" ry="7.6875"/>');
|
||||
});
|
||||
});
|
||||
11
packages/ui-identicon/src/svg/svg.js
Normal file
11
packages/ui-identicon/src/svg/svg.js
Normal file
@ -0,0 +1,11 @@
|
||||
// Copyright 2016 Dan Finlay
|
||||
// Copyright 2017-2018 Jaco Greeff
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
// @flow
|
||||
|
||||
const SVG_NS = 'http://www.w3.org/2000/svg';
|
||||
|
||||
module.exports = function svg (type: string): Element {
|
||||
return document.createElementNS(SVG_NS, type);
|
||||
};
|
||||
17
packages/ui-identicon/src/svg/svg.spec.js
Normal file
17
packages/ui-identicon/src/svg/svg.spec.js
Normal file
@ -0,0 +1,17 @@
|
||||
// Copyright 2017-2018 Jaco Greeff
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
|
||||
const xmlserializer = require('xmlserializer');
|
||||
|
||||
const svg = require('./svg');
|
||||
|
||||
describe('svg', () => {
|
||||
it('creates a basic SVG element', () => {
|
||||
expect(
|
||||
xmlserializer.serializeToString(
|
||||
svg('rect')
|
||||
)
|
||||
).toEqual('<rect xmlns="http://www.w3.org/2000/svg"/>');
|
||||
});
|
||||
});
|
||||
6
packages/ui-identicon/src/types.js
Normal file
6
packages/ui-identicon/src/types.js
Normal file
@ -0,0 +1,6 @@
|
||||
// Copyright 2017-2018 Jaco Greeff
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
// @flow
|
||||
|
||||
export type Seeder = () => number;
|
||||
26
packages/ui-identicon/webpack.config.js
Normal file
26
packages/ui-identicon/webpack.config.js
Normal file
@ -0,0 +1,26 @@
|
||||
// Copyright 2017-2018 Jaco Greeff
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
// @flow
|
||||
|
||||
module.exports = {
|
||||
context: __dirname,
|
||||
entry: './src/demo.js',
|
||||
mode: 'development',
|
||||
output: {
|
||||
path: __dirname,
|
||||
filename: './demo.js'
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.js$/,
|
||||
exclude: /(node_modules)/,
|
||||
use: [
|
||||
'babel-loader'
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
plugins: []
|
||||
};
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@polkadot/ui-keyring",
|
||||
"version": "0.7.2",
|
||||
"version": "0.17.0",
|
||||
"main": "index.js",
|
||||
"repository": "https://github.com/polkadot-js/apps.git",
|
||||
"author": "Jaco Greeff <jacogr@gmail.com>",
|
||||
@ -12,7 +12,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.0.0-beta.47",
|
||||
"@polkadot/ui-react": "^0.14.3",
|
||||
"@polkadot/ui-react": "^0.17.0",
|
||||
"@polkadot/util-keyring": "^0.22.2",
|
||||
"store": "^2.0.12"
|
||||
},
|
||||
|
||||
15
packages/ui-react-rx/LICENSE
Normal file
15
packages/ui-react-rx/LICENSE
Normal file
@ -0,0 +1,15 @@
|
||||
ISC License (ISC)
|
||||
|
||||
Copyright 2017-2018 Jaco Greeff <jacogr@gmail.com>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
PERFORMANCE OF THIS SOFTWARE.
|
||||
46
packages/ui-react-rx/README.md
Normal file
46
packages/ui-react-rx/README.md
Normal file
@ -0,0 +1,46 @@
|
||||
[](https://polkadot.js.org)
|
||||

|
||||
[](https://github.com/Flet/semistandard)
|
||||
[](https://www.npmjs.com/package/@polkadot/ui-react-rx)
|
||||
[](https://travis-ci.org/polkadot-js/ui)
|
||||
[](https://codeclimate.com/github/polkadot-js/apps/maintainability)
|
||||
[](https://coveralls.io/github/polkadot-js/apps?branch=master)
|
||||
[](https://david-dm.org/polkadot-js/apps?path=packages/ui-react-rx)
|
||||
[](https://david-dm.org/polkadot-js/apps?path=packages/ui-react-rx#info=devDependencies)
|
||||
|
||||
# @polkadot/ui-react-rx
|
||||
|
||||
A collection of RxJS enabled React components that operate with the [@polkadot/api-rx](https://github.com/polkadot-js/api) library. It automatically manages subscriptions on behalf of the developer, providing a number of unstyled components that can be used to construct UIs.
|
||||
|
||||
## usage
|
||||
|
||||
Basic usage entails creating a `ContextProvider` and just using the components. For instance, to display the current node time,
|
||||
|
||||
```js
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
|
||||
import { Api, NodeTime } from '@polkadot/ui-react-rx';
|
||||
|
||||
...
|
||||
ReactDOM.render(
|
||||
<Api>
|
||||
<NodeTime />
|
||||
</Api>,
|
||||
document.querySelector('#container')
|
||||
);
|
||||
...
|
||||
```
|
||||
|
||||
All components are provided unstyled making no assumptions on the actual use, however they all support (optionally) the `label`, `className` and `style` attributes, that can be used to style to component.
|
||||
|
||||
```js
|
||||
...
|
||||
ReactDOM.render(
|
||||
<Api>
|
||||
<NodeTime className='rx-time' label='current node time:' />
|
||||
</Api>,
|
||||
document.querySelector('#container')
|
||||
);
|
||||
...
|
||||
```
|
||||
14
packages/ui-react-rx/index.html
Normal file
14
packages/ui-react-rx/index.html
Normal file
@ -0,0 +1,14 @@
|
||||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/normalize/5.0.0/normalize.min.css" />
|
||||
<style>
|
||||
#demo {
|
||||
padding: 0.5em;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="demo"></div>
|
||||
<script src="./demo.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
50
packages/ui-react-rx/package.json
Normal file
50
packages/ui-react-rx/package.json
Normal file
@ -0,0 +1,50 @@
|
||||
{
|
||||
"name": "@polkadot/ui-react-rx",
|
||||
"version": "0.17.0",
|
||||
"description": "A collection of RxJs React components the Polkadot JS API",
|
||||
"main": "index.js",
|
||||
"keywords": [
|
||||
"Polkadot",
|
||||
"React",
|
||||
"RxJs"
|
||||
],
|
||||
"author": "Jaco Greeff <jacogr@gmail.com>",
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": ">=8.0"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
"registry": "https://registry.npmjs.org"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/polkadot-js/ui.git"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/polkadot-js/ui/issues"
|
||||
},
|
||||
"homepage": "https://github.com/polkadot-js/ui/tree/master/packages/ui-react-rx#readme",
|
||||
"scripts": {
|
||||
"build": "polkadot-dev-build-babel",
|
||||
"check": "eslint src && flow check",
|
||||
"test": "echo \"Tests only available from root wrapper\""
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.0.0-beta.47",
|
||||
"@polkadot/api-rx": "^0.13.2",
|
||||
"@polkadot/extrinsics-codec": "^0.17.2",
|
||||
"@polkadot/storage": "^0.17.2",
|
||||
"semver": "^5.5.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-loader": "^8.0.0-beta.2",
|
||||
"enzyme": "^3.3.0",
|
||||
"enzyme-adapter-react-16": "^1.1.1",
|
||||
"react": "^16.4.0",
|
||||
"react-dom": "^16.4.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.4.0"
|
||||
}
|
||||
}
|
||||
134
packages/ui-react-rx/src/Api.js
Normal file
134
packages/ui-react-rx/src/Api.js
Normal file
@ -0,0 +1,134 @@
|
||||
// Copyright 2017-2018 Jaco Greeff
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
// @flow
|
||||
|
||||
import type { ProviderInterface } from '@polkadot/api-provider/types';
|
||||
import type { RxApiInterface } from '@polkadot/api-rx/types';
|
||||
import type { EncodingVersions } from '@polkadot/extrinsics-codec/types';
|
||||
import type { ApiProps } from './types';
|
||||
|
||||
import React from 'react';
|
||||
import semver from 'semver';
|
||||
import createWsProvider from '@polkadot/api-provider/ws';
|
||||
import createApi from '@polkadot/api-rx';
|
||||
import defaults from '@polkadot/api-rx/defaults';
|
||||
|
||||
type Props = {
|
||||
api?: RxApiInterface,
|
||||
children: React$Node,
|
||||
provider?: ProviderInterface,
|
||||
url?: string
|
||||
};
|
||||
|
||||
type State = ApiProps & {
|
||||
subscriptions: Array<rxjs$ISubscription | null>;
|
||||
};
|
||||
|
||||
const { Consumer, Provider } = React.createContext();
|
||||
|
||||
function apiSupport (version?: string): EncodingVersions {
|
||||
return version === undefined || semver.lt(version, '0.2.0')
|
||||
? 'poc-1'
|
||||
: 'latest';
|
||||
}
|
||||
|
||||
export default class Api extends React.Component<Props, State> {
|
||||
static Provider = Provider;
|
||||
static Consumer = Consumer
|
||||
|
||||
state: State;
|
||||
|
||||
constructor (props: Props) {
|
||||
super(props);
|
||||
|
||||
const { provider, url = '' } = props;
|
||||
const api = props.api || createApi(
|
||||
url && url.length
|
||||
? createWsProvider(url)
|
||||
: provider
|
||||
);
|
||||
const setApi = (api: RxApiInterface): void => {
|
||||
this.setState({ api }, () => {
|
||||
this.updateSubscriptions();
|
||||
});
|
||||
};
|
||||
const setApiProvider = (provider?: ProviderInterface): void =>
|
||||
setApi(createApi(provider));
|
||||
const setApiWsUrl = (url?: string = defaults.WS_URL): void =>
|
||||
setApiProvider(createWsProvider(url));
|
||||
|
||||
this.state = {
|
||||
api,
|
||||
apiConnected: false,
|
||||
apiSupport: 'poc-1',
|
||||
setApi,
|
||||
setApiProvider,
|
||||
setApiWsUrl,
|
||||
subscriptions: []
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount () {
|
||||
this.updateSubscriptions();
|
||||
}
|
||||
|
||||
componentWillUnmount () {
|
||||
this.unsubscribe();
|
||||
}
|
||||
|
||||
updateSubscriptions () {
|
||||
const { api } = this.state;
|
||||
|
||||
this.unsubscribe();
|
||||
this.setState({
|
||||
subscriptions:
|
||||
[
|
||||
() => api.isConnected().subscribe((isConnected?: boolean) => {
|
||||
this.setState({ apiConnected: !!isConnected });
|
||||
}),
|
||||
() => api.system.version().subscribe((version?: string) => {
|
||||
this.setState({ apiSupport: apiSupport(version) });
|
||||
})
|
||||
].map((fn: () => rxjs$ISubscription): rxjs$ISubscription | null => {
|
||||
try {
|
||||
return fn();
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
return null;
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
unsubscribe (): void {
|
||||
const { subscriptions } = this.state;
|
||||
|
||||
subscriptions.forEach((subscription) => {
|
||||
if (subscription) {
|
||||
try {
|
||||
subscription.unsubscribe();
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
render () {
|
||||
const { api, apiConnected, apiSupport, setApi, setApiProvider, setApiWsUrl } = this.state;
|
||||
|
||||
return (
|
||||
<Provider value={{
|
||||
api,
|
||||
apiConnected,
|
||||
apiSupport,
|
||||
setApi,
|
||||
setApiProvider,
|
||||
setApiWsUrl
|
||||
}}>
|
||||
{this.props.children}
|
||||
</Provider>
|
||||
);
|
||||
}
|
||||
}
|
||||
14
packages/ui-react-rx/src/Balance.js
Normal file
14
packages/ui-react-rx/src/Balance.js
Normal file
@ -0,0 +1,14 @@
|
||||
// Copyright 2017-2018 Jaco Greeff
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
// @flow
|
||||
|
||||
import storage from '@polkadot/storage';
|
||||
|
||||
import numberFormat from './util/numberFormat';
|
||||
import withStorageDiv from './with/storageDiv';
|
||||
|
||||
export default withStorageDiv(storage.staking.public.freeBalanceOf)(
|
||||
numberFormat,
|
||||
{ className: 'rx--Balance' }
|
||||
);
|
||||
20
packages/ui-react-rx/src/BestNumber.js
Normal file
20
packages/ui-react-rx/src/BestNumber.js
Normal file
@ -0,0 +1,20 @@
|
||||
// Copyright 2017-2018 Jaco Greeff
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
// @flow
|
||||
|
||||
import type { Header } from '@polkadot/primitives/header';
|
||||
|
||||
import rpcs from '@polkadot/jsonrpc';
|
||||
|
||||
import numberFormat from './util/numberFormat';
|
||||
import withApiDiv from './with/apiDiv';
|
||||
|
||||
export default withApiDiv(rpcs.chain.public.newHead)(
|
||||
(value?: Header): string => {
|
||||
return value && value.number
|
||||
? numberFormat(value.number)
|
||||
: 'unknown';
|
||||
},
|
||||
{ className: 'rx--BestNumber' }
|
||||
);
|
||||
14
packages/ui-react-rx/src/Chain.js
Normal file
14
packages/ui-react-rx/src/Chain.js
Normal file
@ -0,0 +1,14 @@
|
||||
// Copyright 2017-2018 Jaco Greeff
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
// @flow
|
||||
|
||||
import rpcs from '@polkadot/jsonrpc';
|
||||
|
||||
import withApiDiv from './with/apiDiv';
|
||||
|
||||
export default withApiDiv(rpcs.system.public.chain)(
|
||||
(value: string = 'unknown'): string =>
|
||||
value,
|
||||
{ className: 'rx--Chain' }
|
||||
);
|
||||
15
packages/ui-react-rx/src/Connected.js
Normal file
15
packages/ui-react-rx/src/Connected.js
Normal file
@ -0,0 +1,15 @@
|
||||
// Copyright 2017-2018 Jaco Greeff
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
// @flow
|
||||
|
||||
import withApiDiv from './with/apiDiv';
|
||||
|
||||
export default withApiDiv({ name: 'isConnected' })(
|
||||
(value?: boolean = false): string => {
|
||||
return value
|
||||
? 'connected'
|
||||
: 'disconnected';
|
||||
},
|
||||
{ className: 'rx--Connected' }
|
||||
);
|
||||
30
packages/ui-react-rx/src/Div.js
Normal file
30
packages/ui-react-rx/src/Div.js
Normal file
@ -0,0 +1,30 @@
|
||||
// Copyright 2017-2018 Jaco Greeff
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
// @flow
|
||||
|
||||
import type { BaseProps, BareProps } from './types';
|
||||
|
||||
import React from 'react';
|
||||
|
||||
type Props<T> = BaseProps<T>;
|
||||
|
||||
// FIXME Not 100% on how to push through the generic type when used
|
||||
// flowlint-next-line unclear-type: off
|
||||
const echoFormatter = (value?: any): any =>
|
||||
value;
|
||||
|
||||
export default function Div<T> ({ children, className = 'rx--Div', render = echoFormatter, rxUpdated = false, label = '', style, value }: Props<T>): React$Node {
|
||||
const props: BareProps = {
|
||||
className: [className, rxUpdated ? 'rx--updated' : undefined]
|
||||
.filter((c) => c)
|
||||
.join(' ') || undefined,
|
||||
style
|
||||
};
|
||||
|
||||
return (
|
||||
<div {...props}>
|
||||
{label}{render(value) || ''}{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
14
packages/ui-react-rx/src/NodeName.js
Normal file
14
packages/ui-react-rx/src/NodeName.js
Normal file
@ -0,0 +1,14 @@
|
||||
// Copyright 2017-2018 Jaco Greeff
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
// @flow
|
||||
|
||||
import rpcs from '@polkadot/jsonrpc';
|
||||
|
||||
import withApiDiv from './with/apiDiv';
|
||||
|
||||
export default withApiDiv(rpcs.system.public.name)(
|
||||
(value: string = 'unknown'): string =>
|
||||
value,
|
||||
{ className: 'rx--NodeName' }
|
||||
);
|
||||
19
packages/ui-react-rx/src/NodeTime.js
Normal file
19
packages/ui-react-rx/src/NodeTime.js
Normal file
@ -0,0 +1,19 @@
|
||||
// Copyright 2017-2018 Jaco Greeff
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
// @flow
|
||||
|
||||
import storage from '@polkadot/storage';
|
||||
|
||||
import withStorageDiv from './with/storageDiv';
|
||||
|
||||
export default withStorageDiv(storage.timestamp.public.current)(
|
||||
(value?: Date): string => {
|
||||
if (!value || value.getTime() === 0) {
|
||||
return 'unknown';
|
||||
}
|
||||
|
||||
return value.toString();
|
||||
},
|
||||
{ className: 'rx--NodeTime' }
|
||||
);
|
||||
14
packages/ui-react-rx/src/NodeVersion.js
Normal file
14
packages/ui-react-rx/src/NodeVersion.js
Normal file
@ -0,0 +1,14 @@
|
||||
// Copyright 2017-2018 Jaco Greeff
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
// @flow
|
||||
|
||||
import rpcs from '@polkadot/jsonrpc';
|
||||
|
||||
import withApiDiv from './with/apiDiv';
|
||||
|
||||
export default withApiDiv(rpcs.system.public.version)(
|
||||
(value: string = 'unknown'): string =>
|
||||
value,
|
||||
{ className: 'rx--NodeVersion' }
|
||||
);
|
||||
14
packages/ui-react-rx/src/Nonce.js
Normal file
14
packages/ui-react-rx/src/Nonce.js
Normal file
@ -0,0 +1,14 @@
|
||||
// Copyright 2017-2018 Jaco Greeff
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
// @flow
|
||||
|
||||
import storage from '@polkadot/storage';
|
||||
|
||||
import numberFormat from './util/numberFormat';
|
||||
import withStorageDiv from './with/storageDiv';
|
||||
|
||||
export default withStorageDiv(storage.system.public.accountIndexOf)(
|
||||
numberFormat,
|
||||
{ className: 'rx--Nonce' }
|
||||
);
|
||||
55
packages/ui-react-rx/src/demo.js
Normal file
55
packages/ui-react-rx/src/demo.js
Normal file
@ -0,0 +1,55 @@
|
||||
// Copyright 2017-2018 Jaco Greeff
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
// @flow
|
||||
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import mockProvider from '@polkadot/api-provider/mock';
|
||||
|
||||
import Api from './Api';
|
||||
import Balance from './Balance';
|
||||
import BestNumber from './BestNumber';
|
||||
import Chain from './Chain';
|
||||
import Connected from './Connected';
|
||||
import NodeName from './NodeName';
|
||||
import NodeTime from './NodeTime';
|
||||
import NodeVersion from './NodeVersion';
|
||||
import Nonce from './Nonce';
|
||||
import withApi from './with/api';
|
||||
|
||||
const provider = mockProvider();
|
||||
|
||||
const ALICE_ADDR = '5GoKvZWG5ZPYL1WUovuHW3zJBWBP5eT8CbqjdRY4Q6iMaDtZ';
|
||||
const ALICE_PUBLIC = '0xd172a74cda4c865912c32ba0a80a57ae69abae410e5ccb59dee84e2f4432db4f';
|
||||
|
||||
const element = document.getElementById('demo');
|
||||
|
||||
if (!element) {
|
||||
throw new Error('Unable to find #demo element');
|
||||
}
|
||||
|
||||
const WithApiDebug = withApi(
|
||||
(apiProps) => (
|
||||
<pre style={{ background: '#f5f5f5', padding: '0.5em' }}>
|
||||
{JSON.stringify(apiProps, undefined, 2)}
|
||||
</pre>
|
||||
)
|
||||
);
|
||||
|
||||
ReactDOM.render(
|
||||
<Api provider={provider}>
|
||||
<Connected label='status =' />
|
||||
<NodeName label='name =' />
|
||||
<NodeVersion label='version =' />
|
||||
<Chain label='chain =' />
|
||||
<br />
|
||||
<BestNumber label='best block #' />
|
||||
<Balance label='balance =' params={[ALICE_PUBLIC]} />
|
||||
<Nonce label='nonce =' params={ALICE_ADDR} />
|
||||
<NodeTime label='node time =' />
|
||||
<br />
|
||||
<WithApiDebug />
|
||||
</Api>,
|
||||
element
|
||||
);
|
||||
30
packages/ui-react-rx/src/index.js
Normal file
30
packages/ui-react-rx/src/index.js
Normal file
@ -0,0 +1,30 @@
|
||||
// Copyright 2017-2018 Jaco Greeff
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
// @flow
|
||||
|
||||
import Api from './Api';
|
||||
import Balance from './Balance';
|
||||
import BestNumber from './BestNumber';
|
||||
import Chain from './Chain';
|
||||
import Connected from './Connected';
|
||||
import Div from './Div';
|
||||
import NodeName from './NodeName';
|
||||
import NodeTime from './NodeTime';
|
||||
import NodeVersion from './NodeVersion';
|
||||
import Nonce from './Nonce';
|
||||
|
||||
export * from './with';
|
||||
|
||||
export {
|
||||
Api,
|
||||
Balance,
|
||||
BestNumber,
|
||||
Chain,
|
||||
Connected,
|
||||
Div,
|
||||
NodeName,
|
||||
NodeTime,
|
||||
NodeVersion,
|
||||
Nonce
|
||||
};
|
||||
48
packages/ui-react-rx/src/types.js
Normal file
48
packages/ui-react-rx/src/types.js
Normal file
@ -0,0 +1,48 @@
|
||||
// Copyright 2017-2018 Jaco Greeff
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
// @flow
|
||||
|
||||
import type { ProviderInterface } from '@polkadot/api-provider/types';
|
||||
import type { RxApiInterface } from '@polkadot/api-rx/types';
|
||||
import type { EncodingVersions } from '@polkadot/extrinsics-codec/types';
|
||||
import type { Storage$Key$Values } from '@polkadot/storage/types';
|
||||
|
||||
export type BareProps = {
|
||||
className?: string,
|
||||
style?: { [string]: mixed }
|
||||
};
|
||||
|
||||
export type ApiProps = {
|
||||
api: RxApiInterface,
|
||||
apiConnected: boolean,
|
||||
apiSupport: EncodingVersions,
|
||||
setApi: (api: RxApiInterface) => void,
|
||||
setApiProvider: (provider?: ProviderInterface) => void,
|
||||
setApiWsUrl: (url?: string) => void
|
||||
};
|
||||
|
||||
export type OnChangeCb<T> = rxjs$Subject<T> | (value: T) => void;
|
||||
|
||||
export type ChangeProps<T> = {
|
||||
onChange?: OnChangeCb<T>
|
||||
};
|
||||
|
||||
export type ParamProps = {
|
||||
params?: Storage$Key$Values
|
||||
};
|
||||
|
||||
export type RxProps<T> = {
|
||||
rxUpdated?: boolean;
|
||||
rxUpdatedAt?: number;
|
||||
value?: T;
|
||||
}
|
||||
|
||||
export type BaseProps<T> = BareProps & ApiProps & ChangeProps<T> & ParamProps & RxProps<T> & {
|
||||
children?: React$Node,
|
||||
label?: string,
|
||||
render?: (value?: T) => React$Node
|
||||
};
|
||||
|
||||
// flowlint-next-line unclear-type:off
|
||||
export type Formatter = (value?: any) => string;
|
||||
22
packages/ui-react-rx/src/util/intervalSubscribe.js
Normal file
22
packages/ui-react-rx/src/util/intervalSubscribe.js
Normal file
@ -0,0 +1,22 @@
|
||||
// Copyright 2017-2018 Jaco Greeff
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
// @flow
|
||||
|
||||
import type { RxProps } from '../types';
|
||||
|
||||
import { interval } from 'rxjs/observable/interval';
|
||||
|
||||
const interval$ = interval(500);
|
||||
|
||||
export default function intervalSubscribe<T, Props, State: RxProps<T>> (that: React$Component<Props, State>): rxjs$ISubscription {
|
||||
return interval$.subscribe(() => {
|
||||
const rxUpdated = (Date.now() - (that.state.rxUpdatedAt || 0)) <= 1500;
|
||||
|
||||
if (rxUpdated !== that.state.rxUpdated) {
|
||||
that.setState({
|
||||
rxUpdated
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
8
packages/ui-react-rx/src/util/isEqual.js
Normal file
8
packages/ui-react-rx/src/util/isEqual.js
Normal file
@ -0,0 +1,8 @@
|
||||
// Copyright 2017-2018 Jaco Greeff
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
// @flow
|
||||
|
||||
export default function isEqual <T> (a?: T, b?: T): boolean {
|
||||
return JSON.stringify({ test: a }) === JSON.stringify({ test: b });
|
||||
}
|
||||
16
packages/ui-react-rx/src/util/numberFormat.js
Normal file
16
packages/ui-react-rx/src/util/numberFormat.js
Normal file
@ -0,0 +1,16 @@
|
||||
// Copyright 2017-2018 Jaco Greeff
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
// @flow
|
||||
|
||||
import type BN from 'bn.js';
|
||||
|
||||
const NUMBER_REGEX = new RegExp('(\\d+?)(?=(\\d{3})+(?!\\d)|$)', 'g');
|
||||
|
||||
export default function numberFormat (value?: BN | number): string {
|
||||
if (value === undefined) {
|
||||
return '0';
|
||||
}
|
||||
|
||||
return (value.toString().match(NUMBER_REGEX) || []).join(',');
|
||||
}
|
||||
25
packages/ui-react-rx/src/util/triggerChange.js
Normal file
25
packages/ui-react-rx/src/util/triggerChange.js
Normal file
@ -0,0 +1,25 @@
|
||||
// Copyright 2017-2018 Jaco Greeff
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
// @flow
|
||||
|
||||
import type { OnChangeCb } from '../types';
|
||||
|
||||
import isFunction from '@polkadot/util/is/function';
|
||||
import isObservable from '@polkadot/util/is/observable';
|
||||
|
||||
export default function triggerChange<T> (value?: T, ...onChange: Array<?OnChangeCb<T>>): void {
|
||||
if (!onChange || !onChange.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
onChange.forEach((onChange) => {
|
||||
if (isObservable(onChange)) {
|
||||
// $FlowFixMe type determined
|
||||
onChange.next(value);
|
||||
} else if (isFunction(onChange)) {
|
||||
// $FlowFixMe type determined
|
||||
onChange(value);
|
||||
}
|
||||
});
|
||||
}
|
||||
45
packages/ui-react-rx/src/with/api.js
Normal file
45
packages/ui-react-rx/src/with/api.js
Normal file
@ -0,0 +1,45 @@
|
||||
// Copyright 2017-2018 Jaco Greeff
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
// @flow
|
||||
|
||||
import type { ApiProps, BareProps, ChangeProps } from '../types';
|
||||
import type { DefaultProps } from './types';
|
||||
|
||||
import React from 'react';
|
||||
import assert from '@polkadot/util/assert';
|
||||
|
||||
import Api from '../Api';
|
||||
|
||||
type InProps<T> = BareProps & ChangeProps<T>;
|
||||
|
||||
type OutProps<T> = InProps<T> & ApiProps;
|
||||
|
||||
export default function withApi<T, ComponentProps: $Shape<OutProps<T>>, InputProps: InProps<T>> (Component: React$ComponentType<ComponentProps>, defaultProps?: DefaultProps<T> = {}): Class<React.Component<InputProps>> {
|
||||
return class WithApi extends React.Component<InputProps> {
|
||||
constructor (props: InputProps) {
|
||||
super(props);
|
||||
|
||||
assert(Component, `Expected 'withApi' to wrap a React Component`);
|
||||
}
|
||||
|
||||
render () {
|
||||
return (
|
||||
<Api.Consumer>
|
||||
{(apiProps?: ApiProps): React$Node => {
|
||||
assert(apiProps && apiProps.api, `Application root must be wrapped inside 'rx-react/Api' to provide API context`);
|
||||
|
||||
return (
|
||||
// flowlint-next-line inexact-spread:off
|
||||
<Component
|
||||
{...defaultProps}
|
||||
{...apiProps}
|
||||
{...this.props}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
</Api.Consumer>
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
74
packages/ui-react-rx/src/with/apiCall.js
Normal file
74
packages/ui-react-rx/src/with/apiCall.js
Normal file
@ -0,0 +1,74 @@
|
||||
// Copyright 2017-2018 Jaco Greeff
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
// @flow
|
||||
|
||||
import type { RxApiInterface$Method } from '@polkadot/api-rx/types';
|
||||
import type { ApiProps, BareProps, ChangeProps, ParamProps, RxProps } from '../types';
|
||||
import type { ApiMethod, HOC, Options, DefaultProps } from './types';
|
||||
|
||||
import React from 'react';
|
||||
import assert from '@polkadot/util/assert';
|
||||
|
||||
import isEqual from '../util/isEqual';
|
||||
import withObservable from './observable';
|
||||
import withApi from './api';
|
||||
|
||||
type InProps<T> = BareProps & ChangeProps<T> & ParamProps;
|
||||
|
||||
type WithApiProps<T> = InProps<T> & ApiProps;
|
||||
|
||||
type OutProps<T> = WithApiProps<T> & RxProps<T>;
|
||||
|
||||
type State<ComponentProps> = ParamProps & {
|
||||
Component: React$ComponentType<ComponentProps>,
|
||||
fn: RxApiInterface$Method
|
||||
}
|
||||
|
||||
export default function withApiCall<T, ComponentProps: $Shape<OutProps<T>>, InputProps: InProps<T>, InputApiProps: WithApiProps<T>> ({ name, section }: ApiMethod, options?: Options<T>): HOC<T> {
|
||||
return (Component: React$ComponentType<ComponentProps>, defaultProps?: DefaultProps<T> = {}): Class<React.Component<InputProps>> => {
|
||||
class WithApiCall extends React.Component<InputApiProps, State<InputApiProps>> {
|
||||
state: State<InputApiProps>;
|
||||
|
||||
constructor (props: InputApiProps) {
|
||||
super(props);
|
||||
|
||||
const hasSection = !!section;
|
||||
|
||||
assert(!hasSection || props.api[section], `Unable to find 'api.${section}'`);
|
||||
|
||||
const fn: RxApiInterface$Method = hasSection
|
||||
? props.api[section][name]
|
||||
// $FlowFixMe method check to be done
|
||||
: ((props.api[name]: any): RxApiInterface$Method);
|
||||
|
||||
assert(fn, `Unable to find 'api${hasSection ? '.' : ''}${section || ''}.${name}'`);
|
||||
|
||||
this.state = ({ fn }: $Shape<State<InputApiProps>>);
|
||||
}
|
||||
|
||||
static getDerivedStateFromProps ({ params = [] }: InputApiProps, prevState: State<ComponentProps>): $Shape<State<InputApiProps>> | null {
|
||||
if (isEqual(params, prevState.params)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const observable = prevState.fn.apply(null, params);
|
||||
|
||||
return {
|
||||
Component: withObservable(observable, options)(Component, defaultProps),
|
||||
params
|
||||
};
|
||||
}
|
||||
|
||||
render () {
|
||||
const { Component } = this.state;
|
||||
|
||||
return (
|
||||
<Component {...this.props} />
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return withApi(WithApiCall);
|
||||
};
|
||||
}
|
||||
18
packages/ui-react-rx/src/with/apiDiv.js
Normal file
18
packages/ui-react-rx/src/with/apiDiv.js
Normal file
@ -0,0 +1,18 @@
|
||||
// Copyright 2017-2018 Jaco Greeff
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
// @flow
|
||||
|
||||
import type { BaseProps } from '../types';
|
||||
import type { ApiMethod, ComponentRenderer, DefaultProps, RenderFn, Options } from './types';
|
||||
|
||||
import Div from '../Div';
|
||||
import withApiCall from './apiCall';
|
||||
|
||||
export default function withApiDiv<T, Props: BaseProps<T>> (call: ApiMethod, options?: Options<T>): ComponentRenderer<T> {
|
||||
return (render: RenderFn, defaultProps?: DefaultProps<T> = {}): React$ComponentType<Props> =>
|
||||
withApiCall(call, options)(Div, {
|
||||
...defaultProps,
|
||||
render
|
||||
});
|
||||
}
|
||||
18
packages/ui-react-rx/src/with/index.js
Normal file
18
packages/ui-react-rx/src/with/index.js
Normal file
@ -0,0 +1,18 @@
|
||||
// Copyright 2017-2018 Jaco Greeff
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
// @flow
|
||||
|
||||
import withApi from './api';
|
||||
import withApiCall from './apiCall';
|
||||
import withObservable from './observable';
|
||||
import withObservableParams from './observableParams';
|
||||
import withStorage from './storage';
|
||||
|
||||
export {
|
||||
withApi,
|
||||
withApiCall,
|
||||
withObservable,
|
||||
withObservableParams,
|
||||
withStorage
|
||||
};
|
||||
101
packages/ui-react-rx/src/with/observable.js
Normal file
101
packages/ui-react-rx/src/with/observable.js
Normal file
@ -0,0 +1,101 @@
|
||||
// Copyright 2017-2018 Jaco Greeff
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
// @flow
|
||||
|
||||
// TODO: This is now way more messy and way longer than it should be. Maintainability is lacking - apply some effort and split it into managable parts
|
||||
|
||||
import type { BareProps, ChangeProps, RxProps } from '../types';
|
||||
import type { HOC, Options, DefaultProps } from './types';
|
||||
|
||||
import React from 'react';
|
||||
import { map } from 'rxjs/operators/map';
|
||||
|
||||
import assert from '@polkadot/util/assert';
|
||||
|
||||
import intervalSubscribe from '../util/intervalSubscribe';
|
||||
import isEqual from '../util/isEqual';
|
||||
import triggerChange from '../util/triggerChange';
|
||||
import echoTransform from './transform/echo';
|
||||
|
||||
type InProps<T> = BareProps & ChangeProps<T>;
|
||||
|
||||
type OutProps<T> = InProps<T> & RxProps<T>;
|
||||
|
||||
type State<T> = RxProps<T> & {
|
||||
subscriptions: Array<rxjs$ISubscription>;
|
||||
}
|
||||
|
||||
export default function withObservable<T, ComponentProps: $Shape<OutProps<T>>, InputProps: InProps<T>> (observable: rxjs$Observable<T> | rxjs$Subject<T>, { onChange, propName = 'value', transform = echoTransform }: Options<T> = {}): HOC<T> {
|
||||
return (Component: React$ComponentType<ComponentProps>, defaultProps?: DefaultProps<T> = {}): Class<React.Component<InputProps, State<T>>> =>
|
||||
class WithObservable extends React.Component<InputProps, State<T>> {
|
||||
state: State<T>;
|
||||
|
||||
constructor (props: InputProps) {
|
||||
super(props);
|
||||
|
||||
assert(observable, `Component should have Observable to wrap`);
|
||||
assert(Component, `Expected 'with*' to wrap a React Component`);
|
||||
|
||||
this.state = {
|
||||
rxUpdated: false,
|
||||
rxUpdatedAt: 0,
|
||||
subscriptions: [],
|
||||
value: void 0
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount () {
|
||||
const subscriptions: Array<rxjs$ISubscription> = [
|
||||
observable
|
||||
.pipe(map(transform))
|
||||
.subscribe((value) => {
|
||||
this.triggerUpdate(this.props, value);
|
||||
}),
|
||||
intervalSubscribe(this)
|
||||
];
|
||||
|
||||
this.setState({
|
||||
subscriptions
|
||||
});
|
||||
}
|
||||
|
||||
componentWillUnmount () {
|
||||
this.state.subscriptions.forEach((subscription) => {
|
||||
subscription.unsubscribe();
|
||||
});
|
||||
}
|
||||
|
||||
triggerUpdate = (props: InProps<T>, value?: T): void => {
|
||||
if (isEqual(value, this.state.value)) {
|
||||
return;
|
||||
}
|
||||
|
||||
triggerChange(value, onChange, props.onChange || defaultProps.onChange);
|
||||
|
||||
this.setState({
|
||||
rxUpdated: true,
|
||||
rxUpdatedAt: Date.now(),
|
||||
value
|
||||
});
|
||||
}
|
||||
|
||||
render () {
|
||||
const { rxUpdated, rxUpdatedAt, value } = this.state;
|
||||
// flowlint-next-line inexact-spread:off
|
||||
const _props = {
|
||||
...defaultProps,
|
||||
...this.props,
|
||||
rxUpdated,
|
||||
rxUpdatedAt,
|
||||
[propName]: value
|
||||
};
|
||||
|
||||
delete _props.onChange;
|
||||
|
||||
return (
|
||||
<Component {..._props} />
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
16
packages/ui-react-rx/src/with/observableParams.js
Normal file
16
packages/ui-react-rx/src/with/observableParams.js
Normal file
@ -0,0 +1,16 @@
|
||||
// Copyright 2017-2018 Jaco Greeff
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
// @flow
|
||||
|
||||
import type { HOC, Options } from './types';
|
||||
|
||||
import withObservable from './observable';
|
||||
|
||||
export default function withObservableParams<T> (observable: rxjs$Observable<T> | rxjs$Subject<T>, options?: Options<T> = {}): HOC<T> {
|
||||
return withObservable(observable, {
|
||||
propName: 'params',
|
||||
// flowlint-next-line inexact-spread:off
|
||||
...options
|
||||
});
|
||||
}
|
||||
156
packages/ui-react-rx/src/with/storage.js
Normal file
156
packages/ui-react-rx/src/with/storage.js
Normal file
@ -0,0 +1,156 @@
|
||||
// Copyright 2017-2018 Jaco Greeff
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
// @flow
|
||||
|
||||
// TODO: Lots of duplicated code between this and withObservable, surely there ois a better way of doing this?
|
||||
|
||||
import type { Storage$Key, Storage$Key$Values } from '@polkadot/storage/types';
|
||||
import type { ApiProps, BareProps, ChangeProps, ParamProps, RxProps } from '../types';
|
||||
import type { HOC, StorageOptions, DefaultProps, Transform } from './types';
|
||||
|
||||
import React from 'react';
|
||||
import { map } from 'rxjs/operators/map';
|
||||
|
||||
import storageKey from '@polkadot/storage/key';
|
||||
|
||||
import intervalSubscribe from '../util/intervalSubscribe';
|
||||
import isEqual from '../util/isEqual';
|
||||
import triggerChange from '../util/triggerChange';
|
||||
import echoTransform from './transform/echo';
|
||||
import createTransform from './transform/storage';
|
||||
import withApi from './api';
|
||||
|
||||
type InProps<T> = BareProps & ChangeProps<T> & ParamProps & {
|
||||
transform?: Transform
|
||||
};
|
||||
|
||||
type InApiProps<T> = ApiProps & InProps<T>;
|
||||
|
||||
type OutProps<T> = InApiProps<T> & RxProps<T>;
|
||||
|
||||
type State<T> = RxProps<T> & {
|
||||
subscriptions: Array<rxjs$ISubscription>;
|
||||
}
|
||||
|
||||
export default function withStorage<T, ComponentProps: $Shape<OutProps<T>>, InputProps: InProps<T>, InputApiProps: InApiProps<T>> (key: Storage$Key, { onChange, params, propName = 'value', transform }: StorageOptions<T> = {}): HOC<T> {
|
||||
const keyCreator = storageKey(key);
|
||||
const storageTransform = createTransform(key);
|
||||
const createKey = (propParams?: Storage$Key$Values): Uint8Array => {
|
||||
const values = [params, propParams].reduce((result, input = []) => {
|
||||
if (Array.isArray(input)) {
|
||||
return result.concat(input);
|
||||
}
|
||||
|
||||
result.push(input);
|
||||
return result;
|
||||
}, []);
|
||||
|
||||
return keyCreator.apply(null, values);
|
||||
};
|
||||
|
||||
return (Component: React$ComponentType<ComponentProps>, defaultProps?: DefaultProps<T> = {}): Class<React.Component<InputProps>> => {
|
||||
class WithStorage extends React.Component<InputApiProps, State<T>> {
|
||||
state: State<T>;
|
||||
|
||||
constructor (props: InputApiProps) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
rxUpdated: false,
|
||||
rxUpdatedAt: 0,
|
||||
subscriptions: [],
|
||||
value: void 0
|
||||
};
|
||||
}
|
||||
|
||||
componentDidUpdate (prevProps: InputApiProps) {
|
||||
if (!isEqual(this.props.params, prevProps.params)) {
|
||||
this.triggerUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount () {
|
||||
const subscriptions: Array<rxjs$ISubscription> = [
|
||||
this.props.api.chain
|
||||
.newHead()
|
||||
.subscribe((value) => {
|
||||
this.triggerUpdate();
|
||||
}),
|
||||
intervalSubscribe(this)
|
||||
];
|
||||
|
||||
this.setState({
|
||||
subscriptions
|
||||
});
|
||||
}
|
||||
|
||||
componentWillUnmount () {
|
||||
this.state.subscriptions.forEach((subscription) => {
|
||||
subscription.unsubscribe();
|
||||
});
|
||||
}
|
||||
|
||||
triggerUpdate = async (): Promise<void> => {
|
||||
// flowlint-next-line unclear-type:off
|
||||
const mapping = (value: Uint8Array, index: number): any => {
|
||||
const outer = this.props.transform || echoTransform;
|
||||
const inner = transform || echoTransform;
|
||||
|
||||
return outer(
|
||||
inner(
|
||||
storageTransform(value, index),
|
||||
index
|
||||
),
|
||||
index
|
||||
);
|
||||
};
|
||||
|
||||
const key = createKey(this.props.params);
|
||||
let value;
|
||||
|
||||
try {
|
||||
value = await this.props.api.state
|
||||
.getStorage(key)
|
||||
.pipe(map(mapping))
|
||||
.toPromise();
|
||||
} catch (error) {
|
||||
// swallow?
|
||||
// console.error(error);
|
||||
}
|
||||
|
||||
if (isEqual(value, this.state.value)) {
|
||||
return;
|
||||
}
|
||||
|
||||
triggerChange(value, onChange, this.props.onChange || defaultProps.onChange);
|
||||
|
||||
this.setState({
|
||||
rxUpdated: true,
|
||||
rxUpdatedAt: Date.now(),
|
||||
value
|
||||
});
|
||||
}
|
||||
|
||||
render () {
|
||||
const { rxUpdated, rxUpdatedAt, value } = this.state;
|
||||
// flowlint-next-line inexact-spread:off
|
||||
const _props = {
|
||||
...defaultProps,
|
||||
...this.props,
|
||||
rxUpdated,
|
||||
rxUpdatedAt,
|
||||
[propName]: value
|
||||
};
|
||||
|
||||
delete _props.onChange;
|
||||
|
||||
return (
|
||||
<Component {..._props} />
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return withApi(WithStorage);
|
||||
};
|
||||
}
|
||||
19
packages/ui-react-rx/src/with/storageDiv.js
Normal file
19
packages/ui-react-rx/src/with/storageDiv.js
Normal file
@ -0,0 +1,19 @@
|
||||
// Copyright 2017-2018 Jaco Greeff
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
// @flow
|
||||
|
||||
import type { Storage$Key } from '@polkadot/storage/types';
|
||||
import type { BaseProps } from '../types';
|
||||
import type { ComponentRenderer, DefaultProps, RenderFn, StorageOptions } from './types';
|
||||
|
||||
import Div from '../Div';
|
||||
import withStorage from './storage';
|
||||
|
||||
export default function withStorageDiv<T, Props: BaseProps<T>> (key: Storage$Key, options?: StorageOptions<T>): ComponentRenderer<T> {
|
||||
return (render: RenderFn, defaultProps?: DefaultProps<T> = {}): React$ComponentType<Props> =>
|
||||
withStorage(key, options)(Div, {
|
||||
...defaultProps,
|
||||
render
|
||||
});
|
||||
}
|
||||
8
packages/ui-react-rx/src/with/transform/echo.js
Normal file
8
packages/ui-react-rx/src/with/transform/echo.js
Normal file
@ -0,0 +1,8 @@
|
||||
// Copyright 2017-2018 Jaco Greeff
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
// @flow
|
||||
|
||||
export default function echoTransform <T> (x: T, index: number): T {
|
||||
return x;
|
||||
}
|
||||
21
packages/ui-react-rx/src/with/transform/storage.js
Normal file
21
packages/ui-react-rx/src/with/transform/storage.js
Normal file
@ -0,0 +1,21 @@
|
||||
// Copyright 2017-2018 Jaco Greeff
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
// @flow
|
||||
|
||||
import type { Param$Values } from '@polkadot/params/types';
|
||||
import type { Storage$Key } from '@polkadot/storage/types';
|
||||
import type { StorageTransform } from '../types';
|
||||
|
||||
import decodeParams from '@polkadot/params/decode';
|
||||
|
||||
export default function storageTransform ({ type }: Storage$Key): StorageTransform {
|
||||
return (input?: Uint8Array = new Uint8Array(0), index: number): Param$Values | null => {
|
||||
try {
|
||||
return decodeParams(type, input).value;
|
||||
} catch (error) {
|
||||
console.error('Decoding', type, '::', error);
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
42
packages/ui-react-rx/src/with/types.js
Normal file
42
packages/ui-react-rx/src/with/types.js
Normal file
@ -0,0 +1,42 @@
|
||||
// Copyright 2017-2018 Jaco Greeff
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
// @flow
|
||||
|
||||
import type { Interface$Method } from '@polkadot/jsonrpc/types';
|
||||
import type { Param$Values } from '@polkadot/params/types';
|
||||
import type { Storage$Key$Values } from '@polkadot/storage/types';
|
||||
import type { OnChangeCb } from '../types';
|
||||
|
||||
import React from 'react';
|
||||
|
||||
// flowlint-next-line unclear-type:off
|
||||
export type Transform = (value: any, index: number) => any;
|
||||
|
||||
export type DefaultProps<T> = {
|
||||
onChange?: OnChangeCb<T>,
|
||||
[string]: mixed
|
||||
};
|
||||
|
||||
export type Options<T> = {
|
||||
onChange?: OnChangeCb<T>,
|
||||
propName?: string,
|
||||
transform?: Transform
|
||||
};
|
||||
|
||||
export type StorageTransform = (value?: Uint8Array, index: number) => Param$Values | null;
|
||||
|
||||
export type StorageOptions<T> = Options<T> & {
|
||||
params?: Storage$Key$Values;
|
||||
};
|
||||
|
||||
// flowlint-next-line unclear-type:off
|
||||
export type HOC<T> = (Component: React$ComponentType<any>, defaultProps?: DefaultProps<T>) => Class<React.Component<any, any>>;
|
||||
|
||||
export type ApiMethod = $Shape<Interface$Method>;
|
||||
|
||||
// flowlint-next-line unclear-type:off
|
||||
export type RenderFn = (value?: any) => any;
|
||||
|
||||
// flowlint-next-line unclear-type:off
|
||||
export type ComponentRenderer<T> = (render: RenderFn, defaultProps?: DefaultProps<T>) => React$ComponentType<any>;
|
||||
12
packages/ui-react-rx/test/enzyme.js
Normal file
12
packages/ui-react-rx/test/enzyme.js
Normal file
@ -0,0 +1,12 @@
|
||||
// Copyright 2017-2018 Jaco Greeff
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
|
||||
const Adapter = require('enzyme-adapter-react-16');
|
||||
const Enzyme = require('enzyme');
|
||||
|
||||
Enzyme.configure({
|
||||
adapter: new Adapter()
|
||||
});
|
||||
|
||||
module.exports = Enzyme;
|
||||
15
packages/ui-react-rx/test/observable.js
Normal file
15
packages/ui-react-rx/test/observable.js
Normal file
@ -0,0 +1,15 @@
|
||||
// Copyright 2017-2018 Jaco Greeff
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
|
||||
const createObservable = require('@polkadot/api-rx/observable');
|
||||
|
||||
module.exports = function observable (method) {
|
||||
const fn = () => Promise.resolve(12345);
|
||||
|
||||
fn.unsubscribe = () => Promise.resolve(true);
|
||||
|
||||
return createObservable(`section_${method}`, method, {
|
||||
[method]: fn
|
||||
})();
|
||||
};
|
||||
29
packages/ui-react-rx/webpack.config.js
Normal file
29
packages/ui-react-rx/webpack.config.js
Normal file
@ -0,0 +1,29 @@
|
||||
// Copyright 2017-2018 Jaco Greeff
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
// @flow
|
||||
|
||||
module.exports = {
|
||||
context: __dirname,
|
||||
entry: './src/demo.js',
|
||||
mode: 'development',
|
||||
output: {
|
||||
path: __dirname,
|
||||
filename: './demo.js'
|
||||
},
|
||||
resolve: {
|
||||
alias: {}
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.js$/,
|
||||
exclude: /(node_modules)/,
|
||||
use: {
|
||||
loader: 'babel-loader'
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
plugins: []
|
||||
};
|
||||
15
packages/ui-react/LICENSE
Normal file
15
packages/ui-react/LICENSE
Normal file
@ -0,0 +1,15 @@
|
||||
ISC License (ISC)
|
||||
|
||||
Copyright 2017-2018 Jaco Greeff <jacogr@gmail.com>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
PERFORMANCE OF THIS SOFTWARE.
|
||||
13
packages/ui-react/README.md
Normal file
13
packages/ui-react/README.md
Normal file
@ -0,0 +1,13 @@
|
||||
[](https://polkadot.js.org)
|
||||

|
||||
[](https://github.com/Flet/semistandard)
|
||||
[](https://www.npmjs.com/package/@polkadot/ui-react)
|
||||
[](https://travis-ci.org/polkadot-js/ui)
|
||||
[](https://codeclimate.com/github/polkadot-js/apps/maintainability)
|
||||
[](https://coveralls.io/github/polkadot-js/apps?branch=master)
|
||||
[](https://david-dm.org/polkadot-js/apps?path=packages/ui-react)
|
||||
[](https://david-dm.org/polkadot-js/apps?path=packages/ui-react#info=devDependencies)
|
||||
|
||||
# @polkadot/ui-react
|
||||
|
||||
A lightweight collection of React components used for construction of applications. See [src/demo.js](src/demo.js) for a sample utilising all components.
|
||||
9
packages/ui-react/index.html
Normal file
9
packages/ui-react/index.html
Normal file
@ -0,0 +1,9 @@
|
||||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/normalize/5.0.0/normalize.min.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="demo"></div>
|
||||
<script src="./demo.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
35
packages/ui-react/package.json
Normal file
35
packages/ui-react/package.json
Normal file
@ -0,0 +1,35 @@
|
||||
{
|
||||
"name": "@polkadot/ui-react",
|
||||
"version": "0.17.0",
|
||||
"description": "A collection of base React components",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"build": "polkadot-dev-build-babel",
|
||||
"check": "stylelint 'src/**/*.css' && eslint src && flow check",
|
||||
"test": "echo \"Tests only available from root wrapper\""
|
||||
},
|
||||
"author": "Jaco Greeff <jacogr@gmail.com>",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.0.0-beta.47",
|
||||
"@polkadot/ui-identicon": "^0.17.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@polkadot/util-keyring": "^0.22.2",
|
||||
"babel-loader": "^8.0.0-beta.2",
|
||||
"css-loader": "0.28.9",
|
||||
"empty": "^0.10.1",
|
||||
"enzyme": "^3.3.0",
|
||||
"enzyme-adapter-react-16": "^1.1.1",
|
||||
"postcss": "^6.0.22",
|
||||
"postcss-loader": "2.0.10",
|
||||
"postcss-sass": "^0.3.0",
|
||||
"react": "^16.4.0",
|
||||
"react-dom": "^16.4.0",
|
||||
"style-loader": "^0.19.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.4.0",
|
||||
"react-router-dom": "^4.2.2"
|
||||
}
|
||||
}
|
||||
1
packages/ui-react/postcss.config.js
Normal file
1
packages/ui-react/postcss.config.js
Normal file
@ -0,0 +1 @@
|
||||
module.exports = {};
|
||||
18
packages/ui-react/src/Container/Container.css
Normal file
18
packages/ui-react/src/Container/Container.css
Normal file
@ -0,0 +1,18 @@
|
||||
/* Copyright 2017-2018 Jaco Greeff
|
||||
/* This software may be modified and distributed under the terms
|
||||
/* of the ISC license. See the LICENSE file for details. */
|
||||
|
||||
.ui--Container {
|
||||
padding: 0.5em;
|
||||
}
|
||||
|
||||
.ui--DemoContainer {
|
||||
padding: 0.5em 0.5em 1.5em 5em;
|
||||
}
|
||||
|
||||
.ui--DemoHeader {
|
||||
background: #f5f5f5;
|
||||
font-size: 0.75em;
|
||||
margin: 0 0 1em -6em;
|
||||
padding: 0.5em;
|
||||
}
|
||||
39
packages/ui-react/src/Container/Demo.js
Normal file
39
packages/ui-react/src/Container/Demo.js
Normal file
@ -0,0 +1,39 @@
|
||||
// Copyright 2017-2018 Jaco Greeff
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
// @flow
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import Container from './index';
|
||||
|
||||
type DemoProps = {
|
||||
attrs: { [string]: mixed },
|
||||
attrsReq: Array<string>,
|
||||
children: React$Node,
|
||||
component: string
|
||||
};
|
||||
|
||||
export default function Demo ({ attrs, attrsReq, children, component }: DemoProps): React$Node {
|
||||
const allAttrs = Object.assign({ className: '"..."', style: '{...}' }, attrs);
|
||||
const attrsText = Object
|
||||
.keys(allAttrs)
|
||||
.map((attr) => {
|
||||
const value = allAttrs[attr];
|
||||
const bool = value === 'bool';
|
||||
const opt = bool || !attrsReq.includes(attr);
|
||||
|
||||
return `${opt ? '[' : ''}${attr}${bool ? '' : '='}${bool ? '' : value}${opt ? ']' : ''}`;
|
||||
})
|
||||
.sort()
|
||||
.join('\n ');
|
||||
|
||||
return (
|
||||
<Container className='ui--DemoContainer'>
|
||||
<pre className='ui--DemoHeader'>
|
||||
{`<${component}\n ${attrsText}\n/>`}
|
||||
</pre>
|
||||
{children}
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
25
packages/ui-react/src/Container/index.js
Normal file
25
packages/ui-react/src/Container/index.js
Normal file
@ -0,0 +1,25 @@
|
||||
// Copyright 2017-2018 Jaco Greeff
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
// @flow
|
||||
|
||||
import type { BaseProps } from '../types';
|
||||
|
||||
import './Container.css';
|
||||
|
||||
import React from 'react';
|
||||
|
||||
type Props = BaseProps & {
|
||||
children: React$Node
|
||||
};
|
||||
|
||||
export default function Container ({ children, className, style }: Props): React$Node {
|
||||
return (
|
||||
<div
|
||||
className={['ui--Container', className].join(' ')}
|
||||
style={style}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
26
packages/ui-react/src/Container/index.spec.js
Normal file
26
packages/ui-react/src/Container/index.spec.js
Normal file
@ -0,0 +1,26 @@
|
||||
// Copyright 2017-2018 Jaco Greeff
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
|
||||
import { shallow } from '../../test/enzyme';
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import { Container } from '../index';
|
||||
|
||||
describe('Container', () => {
|
||||
let wrapper;
|
||||
|
||||
beforeEach(() => {
|
||||
wrapper = shallow(
|
||||
<Container className='test' />,
|
||||
{}
|
||||
);
|
||||
});
|
||||
|
||||
it('creates the element', () => {
|
||||
expect(
|
||||
wrapper
|
||||
).toBeDefined();
|
||||
});
|
||||
});
|
||||
43
packages/ui-react/src/IdentityIcon/Demo.js
Normal file
43
packages/ui-react/src/IdentityIcon/Demo.js
Normal file
@ -0,0 +1,43 @@
|
||||
// Copyright 2017-2018 Jaco Greeff
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
// @flow
|
||||
|
||||
import React from 'react';
|
||||
import randomAsU8a from '@polkadot/util-crypto/random/asU8a';
|
||||
import addressEncode from '@polkadot/util-keyring/address/encode';
|
||||
|
||||
import Container from '../Container/Demo';
|
||||
import IdentityIcon from './index';
|
||||
|
||||
export default function Demo () {
|
||||
const identities = [new Uint8Array(32)];
|
||||
|
||||
while (identities.length !== 10) {
|
||||
identities.push(
|
||||
addressEncode(randomAsU8a(32))
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Container
|
||||
component='IdentityIcon'
|
||||
attrs={{
|
||||
size: '<number>',
|
||||
value: '{...}'
|
||||
}}
|
||||
attrsReq={[
|
||||
'value'
|
||||
]}
|
||||
>
|
||||
{
|
||||
identities.map((value) => (
|
||||
<IdentityIcon
|
||||
key={value.toString()}
|
||||
value={value}
|
||||
/>
|
||||
))
|
||||
}
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
7
packages/ui-react/src/IdentityIcon/IdentityIcon.css
Normal file
7
packages/ui-react/src/IdentityIcon/IdentityIcon.css
Normal file
@ -0,0 +1,7 @@
|
||||
/* Copyright 2017-2018 Jaco Greeff
|
||||
/* This software may be modified and distributed under the terms
|
||||
/* of the ISC license. See the LICENSE file for details. */
|
||||
|
||||
.ui--IdentityIcon {
|
||||
display: inline-block;
|
||||
}
|
||||
22
packages/ui-react/src/IdentityIcon/appendIcon.js
Normal file
22
packages/ui-react/src/IdentityIcon/appendIcon.js
Normal file
@ -0,0 +1,22 @@
|
||||
// Copyright 2017-2018 Jaco Greeff
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
// @flow
|
||||
|
||||
import identicon from '@polkadot/ui-identicon';
|
||||
|
||||
type NodeRefSet = (node: ?Element) => void;
|
||||
|
||||
export default function appendIcon (address: string | Uint8Array, size: number): NodeRefSet {
|
||||
return (node: ?Element): void => {
|
||||
if (node) {
|
||||
while (node.firstChild) {
|
||||
node.removeChild(node.firstChild);
|
||||
}
|
||||
|
||||
node.appendChild(
|
||||
identicon(address, size)
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
33
packages/ui-react/src/IdentityIcon/appendIcon.spec.js
Normal file
33
packages/ui-react/src/IdentityIcon/appendIcon.spec.js
Normal file
@ -0,0 +1,33 @@
|
||||
// Copyright 2017-2018 Jaco Greeff
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
|
||||
describe('autoFocus', () => {
|
||||
let appendIcon;
|
||||
let mockIdenticon;
|
||||
let node;
|
||||
|
||||
beforeEach(() => {
|
||||
node = {
|
||||
appendChild: jest.fn()
|
||||
};
|
||||
|
||||
mockIdenticon = jest.fn(() => 'genIdentitycon');
|
||||
jest.mock('@polkadot/ui-identicon', () => mockIdenticon);
|
||||
|
||||
appendIcon = require('./appendIcon').default;
|
||||
});
|
||||
|
||||
it('generates & appends icon to the node', () => {
|
||||
appendIcon('testAddr', 'testSize')(node);
|
||||
|
||||
expect(node.appendChild).toHaveBeenCalledWith('genIdentitycon');
|
||||
expect(mockIdenticon).toHaveBeenCalledWith('testAddr', 'testSize');
|
||||
});
|
||||
|
||||
it('does not call identicon when no node', () => {
|
||||
appendIcon('testAddr', 'testSize')();
|
||||
|
||||
expect(mockIdenticon).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
27
packages/ui-react/src/IdentityIcon/index.js
Normal file
27
packages/ui-react/src/IdentityIcon/index.js
Normal file
@ -0,0 +1,27 @@
|
||||
// Copyright 2017-2018 Jaco Greeff
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
// @flow
|
||||
|
||||
import type { BaseProps } from '../types';
|
||||
|
||||
import './IdentityIcon.css';
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import appendIcon from './appendIcon';
|
||||
|
||||
type Props = BaseProps & {
|
||||
size?: number,
|
||||
value: string | Uint8Array
|
||||
};
|
||||
|
||||
export default function IdentityIcon ({ className, size = 64, style, value }: Props) {
|
||||
return (
|
||||
<div
|
||||
className={['ui--IdentityIcon', className].join(' ')}
|
||||
ref={appendIcon(value, size)}
|
||||
style={style}
|
||||
/>
|
||||
);
|
||||
}
|
||||
26
packages/ui-react/src/IdentityIcon/index.spec.js
Normal file
26
packages/ui-react/src/IdentityIcon/index.spec.js
Normal file
@ -0,0 +1,26 @@
|
||||
// Copyright 2017-2018 Jaco Greeff
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
|
||||
import { shallow } from '../../test/enzyme';
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import { IdentityIcon } from '../index';
|
||||
|
||||
describe('IdentityIcon', () => {
|
||||
let wrapper;
|
||||
|
||||
beforeEach(() => {
|
||||
wrapper = shallow(
|
||||
<IdentityIcon className='test' />,
|
||||
{}
|
||||
);
|
||||
});
|
||||
|
||||
it('creates the element', () => {
|
||||
expect(
|
||||
wrapper
|
||||
).toBeDefined();
|
||||
});
|
||||
});
|
||||
8
packages/ui-react/src/demo.css
Normal file
8
packages/ui-react/src/demo.css
Normal file
@ -0,0 +1,8 @@
|
||||
/* Copyright 2017-2018 Jaco Greeff
|
||||
/* This software may be modified and distributed under the terms
|
||||
/* of the ISC license. See the LICENSE file for details. */
|
||||
|
||||
.ui--Demo {
|
||||
margin: 0;
|
||||
padding: 0.5em;
|
||||
}
|
||||
24
packages/ui-react/src/demo.js
Normal file
24
packages/ui-react/src/demo.js
Normal file
@ -0,0 +1,24 @@
|
||||
// Copyright 2017-2018 Jaco Greeff
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
// @flow
|
||||
|
||||
import './demo.css';
|
||||
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
|
||||
import IdentityIcon from './IdentityIcon/Demo';
|
||||
|
||||
const element = document.getElementById('demo');
|
||||
|
||||
if (!element) {
|
||||
throw new Error('Unable to find #demo element');
|
||||
}
|
||||
|
||||
ReactDOM.render(
|
||||
<div className='ui--Demo'>
|
||||
<IdentityIcon />
|
||||
</div>,
|
||||
element
|
||||
);
|
||||
12
packages/ui-react/src/index.js
Normal file
12
packages/ui-react/src/index.js
Normal file
@ -0,0 +1,12 @@
|
||||
// Copyright 2017-2018 Jaco Greeff
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
// @flow
|
||||
|
||||
import Container from './Container';
|
||||
import IdentityIcon from './IdentityIcon';
|
||||
|
||||
export {
|
||||
Container,
|
||||
IdentityIcon
|
||||
};
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user