Initial add from apps

This commit is contained in:
Jaco Greeff
2018-12-05 11:35:28 +01:00
commit 21c47a7d3a
79 changed files with 15785 additions and 0 deletions
+29
View File
@@ -0,0 +1,29 @@
// Copyright 2017-2018 @polkadot/ui-identicon authors & contributors
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import React from 'react';
import { encodeAddress } from '@polkadot/keyring';
import { randomAsU8a } from '@polkadot/util-crypto';
import IdentityIcon from './index';
export default class Demo extends React.PureComponent {
render () {
const identities: Array<string> = [];
while (identities.length !== 10) {
identities.push(
encodeAddress(randomAsU8a(32))
);
}
return identities.map((value) => (
<IdentityIcon
key={value.toString()}
value={value}
/>
));
}
}
+26
View File
@@ -0,0 +1,26 @@
// Copyright 2017-2018 @polkadot/ui-identicon authors & contributors
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import { Props } from './types';
import React from 'react';
export default class Empty extends React.PureComponent<Props> {
render () {
const { className, size, style } = this.props;
return (
<div
className={`container ${className}`}
style={style}
>
<svg
height={size}
viewBox='0 0 64 64'
width={size}
/>
</div>
);
}
}
@@ -0,0 +1,33 @@
/* Copyright 2017-2018 @polkadot/ui-identicon authors & contributors
/* This software may be modified and distributed under the terms
/* of the Apache-2.0 license. See the LICENSE file for details. */
.ui--IdentityIcon {
cursor: copy;
display: inline-block;
line-height: 0;
.container {
position: relative;
> div,
> svg {
position: relative;
}
&:before {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
border-radius: 50%;
box-shadow: 0 0 5px 2px #e0e0e0;
content: '';
}
}
&.highlight > div:before {
box-shadow: 0 0 5px 2px red;
}
}
+140
View File
@@ -0,0 +1,140 @@
// Copyright 2018 Paritytech via paritytech/oo7/polkadot-identicon
// Copyright 2018 @polkadot/ui-identicon authors & contributors
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
// This has been converted from the original version that can be found at
//
// https://github.com/paritytech/oo7/blob/251ba2b7c45503b68eab4320c270b5afa9bccb60/packages/polkadot-identicon/src/index.jsx
//
// Here we have done the following to convert the component -
// - Converted the code to TypeScript
// - Removed the oo7 dependencies (since not initialised properly, it makes additional connections to wrong endpoints)
// - Remove encoding functionality, these are catered for in the base
// - Remove copy functionality (this is catered from in the base components)
// - Move constants to file-level
// - Overall it is now just a static component, expecting an address as an input value
import { Props as BaseProps } from './types';
import React from 'react';
import { decodeAddress } from '@polkadot/keyring';
import blake2b from '@polkadot/util-crypto/blake2/blake2b/asU8a';
type Props = BaseProps & {
sixPoint?: boolean
};
type Scheme = {
freq: number,
colors: Array<number>
};
const s = 64;
const c = s / 2;
const z = s / 64 * 5;
const zero = blake2b(new Uint8Array(32));
const schema: { [index: string]: Scheme } = {
target: { freq: 1, colors: [0, 28, 0, 0, 28, 0, 0, 28, 0, 0, 28, 0, 0, 28, 0, 0, 28, 0, 1] },
cube: { freq: 20, colors: [0, 1, 3, 2, 4, 3, 0, 1, 3, 2, 4, 3, 0, 1, 3, 2, 4, 3, 5] },
quazar: { freq: 16, colors: [1, 2, 3, 1, 2, 4, 5, 5, 4, 1, 2, 3, 1, 2, 4, 5, 5, 4, 0] },
flower: { freq: 32, colors: [0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 3] },
cyclic: { freq: 32, colors: [0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 6] },
vmirror: { freq: 128, colors: [0, 1, 2, 3, 4, 5, 3, 4, 2, 0, 1, 6, 7, 8, 9, 7, 8, 6, 10] },
hmirror: { freq: 128, colors: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 8, 6, 7, 5, 3, 4, 2, 11] }
};
function findScheme (d: number): Scheme {
let cum = 0;
const ks = Object.keys(schema);
for (let i in ks) {
cum += schema[ks[i]].freq;
if (d < cum) {
return schema[ks[i]];
}
}
throw new Error('Unable to find schema');
}
export default class Identicon extends React.PureComponent<Props> {
render () {
const { className, sixPoint = false, size, style, value } = this.props;
const r = sixPoint
? (s / 2 / 8 * 5)
: (s / 2 / 4 * 3);
const rroot3o2 = r * Math.sqrt(3) / 2;
const ro2 = r / 2;
const rroot3o4 = r * Math.sqrt(3) / 4;
const ro4 = r / 4;
const r3o4 = r * 3 / 4;
const total = Object.keys(schema).map(k => schema[k].freq).reduce((a, b) => a + b);
const id = Array.from(blake2b(decodeAddress(value))).map((x, i) =>
(x + 256 - zero[i]) % 256
);
const sat = (Math.floor(id[29] * 70 / 256 + 26) % 80) + 30;
const d = Math.floor((id[30] + id[31] * 256) % total);
const scheme = findScheme(d);
const palette = Array.from(id).map((x, i) => {
const b = (x + i % 28 * 58) % 256;
if (b === 0) {
return '#444';
} else if (b === 255) {
return 'transparent';
}
const h = Math.floor(b % 64 * 360 / 64);
const l = [53, 15, 35, 75][Math.floor(b / 64)];
return `hsl(${h}, ${sat}%, ${l}%)`;
});
const rot = (id[28] % 6) * 3;
const colors = scheme.colors.map((_, i) =>
palette[scheme.colors[i < 18 ? (i + rot) % 18 : 18]]
);
let i = 0;
return (
<div
className={`container ${className}`}
style={style}
>
<svg
id={value}
name={value}
width={size}
height={size}
viewBox='0 0 64 64'
>
<circle cx={s / 2} cy={s / 2} r={s / 2} fill='#eee'/>
<circle cx={c} cy={c - r} r={z} fill={colors[i++]}/>
<circle cx={c} cy={c - ro2} r={z} fill={colors[i++]}/>
<circle cx={c - rroot3o4} cy={c - r3o4} r={z} fill={colors[i++]}/>
<circle cx={c - rroot3o2} cy={c - ro2} r={z} fill={colors[i++]}/>
<circle cx={c - rroot3o4} cy={c - ro4} r={z} fill={colors[i++]}/>
<circle cx={c - rroot3o2} cy={c} r={z} fill={colors[i++]}/>
<circle cx={c - rroot3o2} cy={c + ro2} r={z} fill={colors[i++]}/>
<circle cx={c - rroot3o4} cy={c + ro4} r={z} fill={colors[i++]}/>
<circle cx={c - rroot3o4} cy={c + r3o4} r={z} fill={colors[i++]}/>
<circle cx={c} cy={c + r} r={z} fill={colors[i++]}/>
<circle cx={c} cy={c + ro2} r={z} fill={colors[i++]}/>
<circle cx={c + rroot3o4} cy={c + r3o4} r={z} fill={colors[i++]}/>
<circle cx={c + rroot3o2} cy={c + ro2} r={z} fill={colors[i++]}/>
<circle cx={c + rroot3o4} cy={c + ro4} r={z} fill={colors[i++]}/>
<circle cx={c + rroot3o2} cy={c} r={z} fill={colors[i++]}/>
<circle cx={c + rroot3o2} cy={c - ro2} r={z} fill={colors[i++]}/>
<circle cx={c + rroot3o4} cy={c - ro4} r={z} fill={colors[i++]}/>
<circle cx={c + rroot3o4} cy={c - r3o4} r={z} fill={colors[i++]}/>
<circle cx={c} cy={c} r={z} fill={colors[i++]}/>
</svg>
</div>
);
}
}
+33
View File
@@ -0,0 +1,33 @@
// Copyright 2017-2018 @polkadot/ui-identicon authors & contributors
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import { Props } from './types';
import React from 'react';
import identicon from './beachball';
export default class Substrate extends React.PureComponent<Props> {
render () {
const { className, style } = this.props;
return (
<div
className={`container ${className}`}
ref={this.appendIcon}
style={style}
/>
);
}
private appendIcon = (node: Element | null): void => {
const { size, value } = this.props;
if (node) {
node.appendChild(
identicon(value, size)
);
}
}
}
@@ -0,0 +1,16 @@
Apache-2.0 License (Apache-2.0)
Copyright 2016 Dan Finlay
Copyright 2017-2018 @polkadot/ui-identicon authors & contributors
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 DApache-2.0LAIMS 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.
@@ -0,0 +1,19 @@
# @polkadot/ui-identicon/beachball
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 Apache-2.0)
## Usage
Also see [src/demo.js](src/demo.js) for a randomly generated example.
![demo](https://raw.githubusercontent.com/polkadot-js/ui/master/packages/ui-identicon/demo.png)
@@ -0,0 +1,57 @@
// Copyright 2017-2018 @polkadot/ui-identicon authors & contributors
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import newSeeder from './seeder';
import newColors from './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)'
'hsl(37.19999999999999, 100%, 54.9%)'
);
});
it('applies specified alpha', () => {
expect(
colors(0.5)
).toEqual(
// 'hsla(166.70000000000005, 98.6%, 27.6%, 0.5)'
'hsla(37.19999999999999, 100%, 54.9%, 0.5)'
);
});
it('rolates colors', () => {
colors();
expect(
colors()
).not.toEqual('hsla(166.70000000000005, 98.6%, 27.6%, 0.9)');
});
it('works in edge conditions (0xff)', () => {
const u8a = new Uint8Array(32);
u8a.fill(255);
expect(
colors = newColors(newSeeder(u8a))
).not.toThrow();
expect(
colors()
).toEqual(
// 'hsla(234.39999999999998, 75.9%, 51.2%, 0.9)'
'hsl(15, 0%, 100%)'
);
});
});
@@ -0,0 +1,31 @@
// Copyright 2016 Dan Finlay
// Copyright 2017-2018 @polkadot/ui-identicon authors & contributors
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import { Seeder } from './types';
import Color from 'color';
import { COLORS } from './defaults';
type ColorGen = {
(alpha?: number): string
};
const WOBBLE = 30;
export default function colors (seeder: Seeder): ColorGen {
const amount = (seeder() * WOBBLE) - (WOBBLE / 2);
const all = COLORS.map((hex) =>
Color(hex).rotate(amount)
);
return (alpha: number = 1): string => {
const index = Math.floor(all.length * seeder());
return all.splice(index, 1)[0]
.alpha(alpha)
.string();
};
}
@@ -0,0 +1,43 @@
// Copyright 2017-2018 @polkadot/ui-identicon authors & contributors
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import container from './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');
});
});
@@ -0,0 +1,27 @@
// Copyright 2016 Dan Finlay
// Copyright 2017-2018 @polkadot/ui-identicon authors & contributors
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
export default function container (diameter: number, background: string = 'white', className: string = '', _style: { [index: 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;
Object.keys(style).forEach((key: any) => {
element.style[key] = style[key];
});
return element;
}
@@ -0,0 +1,26 @@
// Copyright 2016 Dan Finlay
// Copyright 2017-2018 @polkadot/ui-identicon authors & contributors
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
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
// https://sashat.me/2017/01/11/list-of-20-simple-distinct-colors/
'#ffe119', '#4363d8', '#f58231', '#fabebe', '#e6beff', '#800000', '#000075', '#a9a9a9', '#ffffff', '#000000'
];
const SHAPE_COUNT = 5;
export {
COLORS,
SHAPE_COUNT
};
@@ -0,0 +1,36 @@
// Copyright 2016 Dan Finlay
// Copyright 2017-2018 @polkadot/ui-identicon authors & contributors
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import { encodeAddress } from '@polkadot/keyring';
import { isNull } from '@polkadot/util';
import { randomAsU8a } from '@polkadot/util-crypto';
import identicon from './index';
const element = document.getElementById('demo');
function generateIcon (seed: string = encodeAddress(randomAsU8a(32))): void {
const start = Date.now();
if (isNull(element)) {
throw new Error('Unable to find #demo element');
}
element.appendChild(
identicon(seed, 100, 'padded')
);
console.log(`Icon generated in ${(Date.now() - start)}ms`);
}
function generateIcons (count: number = 512): void {
generateIcon(encodeAddress(new Uint8Array(32)));
for (let index = 1; index < count; index++) {
generateIcon();
}
}
generateIcons();
@@ -0,0 +1,31 @@
// Copyright 2017-2018 @polkadot/ui-identicon authors & contributors
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import xmlserializer from 'xmlserializer';
import identicon from './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>'
'<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="hsl(212.10000000000002, 65.6%, 55.5%)"/><circle cx="128" cy="153.6" r="102.4" fill="hsl(9.800000000000011, 90.7%, 57.6%)"/><circle cx="128" cy="166.4" r="76.8" fill="hsl(345, 85.7%, 86.3%)"/><circle cx="128" cy="179.2" r="51.2" fill="hsl(261.9, 100%, 87.3%)"/><circle cx="128" cy="192" r="25.6" fill="hsl(345, 100%, 25.1%)"/></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>'
'<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="hsl(212.10000000000002, 65.6%, 55.5%)"/><circle cx="50" cy="60" r="40" fill="hsl(9.800000000000011, 90.7%, 57.6%)"/><circle cx="50" cy="65" r="30" fill="hsl(345, 85.7%, 86.3%)"/><circle cx="50" cy="70" r="20" fill="hsl(261.9, 100%, 87.3%)"/><circle cx="50" cy="75" r="10" fill="hsl(345, 100%, 25.1%)"/></svg></div></div>'
);
});
});
@@ -0,0 +1,31 @@
// Copyright 2016 Dan Finlay
// Copyright 2017-2018 @polkadot/ui-identicon authors & contributors
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import colors from './colors';
import newContainer from './container';
import newSeeder from './seeder';
import newShape from './shape/circle';
import newElement from './svg/element';
import { SHAPE_COUNT } from './defaults';
export default function identicon (seed: string, diameter: number = 256, className: string = '', style?: { [index: 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;
}
@@ -0,0 +1,27 @@
// Copyright 2017-2018 @polkadot/ui-identicon authors & contributors
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import newSeeder from './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);
});
});
@@ -0,0 +1,31 @@
// Copyright 2017-2018 @polkadot/ui-identicon authors & contributors
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import { Seeder } from './types';
import { isU8a, stringToU8a } from '@polkadot/util';
const DIVISOR = 256 * 256;
export default function seeder (_seed: string | Uint8Array = new Uint8Array(32)): Seeder {
const seed: Uint8Array = isU8a(_seed)
? _seed
: stringToU8a(_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;
};
}
@@ -0,0 +1,18 @@
// Copyright 2017-2018 @polkadot/ui-identicon authors & contributors
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import xmlserializer from 'xmlserializer';
import seeder from '../seeder';
import circle from './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"/>');
});
});
@@ -0,0 +1,23 @@
// Copyright 2016 Dan Finlay
// Copyright 2017-2018 @polkadot/ui-identicon authors & contributors
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import { Seeder } from '../types';
import newCircle from '../svg/circle';
import { SHAPE_COUNT } from '../defaults';
export default 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('', 'fill', fill);
return svg;
}
@@ -0,0 +1,26 @@
// Copyright 2016 Dan Finlay
// Copyright 2017-2018 @polkadot/ui-identicon authors & contributors
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import { Seeder } from '../types';
import newRect from '../svg/rect';
import { SHAPE_COUNT } from '../defaults';
export default 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('', 'transform', `translate(${tx} ${ty}) rotate(${rot} ${center} ${center})`);
svg.setAttributeNS('', 'fill', fill);
return svg;
}
@@ -0,0 +1,17 @@
// Copyright 2017-2018 @polkadot/ui-identicon authors & contributors
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import xmlserializer from 'xmlserializer';
import circle from './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"/>');
});
});
@@ -0,0 +1,15 @@
// Copyright 2017-2018 @polkadot/ui-identicon authors & contributors
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import createSvg from './svg';
export default function circle (r: number, cx: number, cy: number): Element {
const elem = createSvg('circle');
elem.setAttributeNS('', 'cx', `${cx}`);
elem.setAttributeNS('', 'cy', `${cy}`);
elem.setAttributeNS('', 'r', `${r}`);
return elem;
}
@@ -0,0 +1,17 @@
// Copyright 2017-2018 @polkadot/ui-identicon authors & contributors
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import xmlserializer from 'xmlserializer';
import element from './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"/>');
});
});
@@ -0,0 +1,17 @@
// Copyright 2016 Dan Finlay
// Copyright 2017-2018 @polkadot/ui-identicon authors & contributors
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import createSvg from './svg';
export default function element (size: number, type: string = 'svg', x: number = 0, y: number = 0): Element {
const elem = createSvg(type);
elem.setAttributeNS('', 'x', `${x}`);
elem.setAttributeNS('', 'y', `${y}`);
elem.setAttributeNS('', 'width', `${size}`);
elem.setAttributeNS('', 'height', `${size}`);
return elem;
}
@@ -0,0 +1,17 @@
// Copyright 2017-2018 @polkadot/ui-identicon authors & contributors
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import xmlserializer from 'xmlserializer';
import rect from './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"/>');
});
});
@@ -0,0 +1,14 @@
// Copyright 2017-2018 @polkadot/ui-identicon authors & contributors
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import createElement from './element';
export default function rect (size: number): Element {
const elem = createElement(size, 'rect');
elem.setAttributeNS('', 'rx', `${size / 16}`);
elem.setAttributeNS('', 'ry', `${size / 16}`);
return elem;
}
@@ -0,0 +1,17 @@
// Copyright 2017-2018 @polkadot/ui-identicon authors & contributors
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import xmlserializer from 'xmlserializer';
import svg from './svg';
describe('svg', () => {
it('creates a basic SVG element', () => {
expect(
xmlserializer.serializeToString(
svg('rect')
)
).toEqual('<rect xmlns="http://www.w3.org/2000/svg"/>');
});
});
@@ -0,0 +1,10 @@
// Copyright 2016 Dan Finlay
// Copyright 2017-2018 @polkadot/ui-identicon authors & contributors
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
const SVG_NS = 'http://www.w3.org/2000/svg';
export default function svg (type: string): Element {
return document.createElementNS(SVG_NS, type);
}
@@ -0,0 +1,5 @@
// Copyright 2017-2018 @polkadot/ui-identicon authors & contributors
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
export type Seeder = () => number;
+103
View File
@@ -0,0 +1,103 @@
// Copyright 2017-2018 @polkadot/ui-identicon authors & contributors
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import { Prefix } from '@polkadot/keyring/address/types';
import { IdentityProps as Props } from './types';
import './IdentityIcon.css';
import React from 'react';
import CopyToClipboard from 'react-copy-to-clipboard';
import { decodeAddress, encodeAddress } from '@polkadot/keyring';
import settings from '@polkadot/ui-settings/index';
import { isHex, isU8a } from '@polkadot/util';
import Empty from './Empty';
import Polkadot from './Polkadot';
import Substrate from './Substrate';
type State = {
address?: string | null
};
const DEFAULT_SIZE = 64;
const Components: { [index: string]: React.ComponentType<any> } = {
'polkadot': Polkadot,
'substrate': Substrate
};
export default class IdentityIcon extends React.PureComponent<Props, State> {
state: State = {
address: null
};
private static prefix?: Prefix = undefined;
static setDefaultPrefix (prefix: Prefix) {
IdentityIcon.prefix = prefix;
}
static getDerivedStateFromProps ({ prefix = IdentityIcon.prefix, value }: Props, prevState: State): State | null {
try {
const address = isU8a(value) || isHex(value)
? encodeAddress(value, prefix)
: value;
decodeAddress(address as string, prefix);
return address === prevState.address
? null
: { address };
} catch (error) {
// swallow,invalid address or input
}
return {
address: null
};
}
render () {
const { className, isHighlight = false, size = DEFAULT_SIZE, style, theme = settings.uiTheme } = this.props;
const { address } = this.state;
const Component = !address
? Empty
: Components[theme] || Substrate;
const wrapped = (
<div
className={['ui--IdentityIcon', isHighlight ? 'highlight' : '', className].join(' ')}
key={address || ''}
style={style}
>
<Component
size={size}
value={address || ''}
/>
</div>
);
if (!address) {
return wrapped;
}
return (
<CopyToClipboard
onCopy={this.onCopy}
text={address}
>
{wrapped}
</CopyToClipboard>
);
}
private onCopy = (): void => {
const { onCopy } = this.props;
const { address } = this.state;
if (address && onCopy) {
onCopy(address);
}
}
}
+26
View File
@@ -0,0 +1,26 @@
// Copyright 2018 @polkadot/ui-identicon authors & contributors
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import { Prefix } from '@polkadot/keyring/address/types';
export type BaseProps = {
className?: string,
style?: {
[index: string]: any
}
};
export type Props = BaseProps & {
size: number,
value: string
};
export type IdentityProps = BaseProps & {
isHighlight?: boolean,
onCopy?: (value: string) => void,
prefix?: Prefix,
size?: number,
theme?: string,
value?: string | Uint8Array | null
};