Add vue-identicon (#178)

* Add vue-identicon

* Remove generated file

* Add links to reactnative & vue identicons

* Update package.json

* Update Jdenticon.vue

* Update Polkadot.vue

* Move deps around

* Empty on error

* Build to build

* ... typo

* Fix vue-identicon doc generation

* Ok, I give up... vuepress and vue packages, no luck

* Swap to TypeScript components (aligning with polkadot-js)

* Expand template with build

* Adjust vue examples

* debump gh-pages dep

* Expand doc. desc.

* Fix vuepress docs generation

* Address CC complexity

* eslint fix (babel config)

* Add Bechball
This commit is contained in:
Jaco Greeff
2019-08-06 13:15:10 +02:00
committed by GitHub
parent 32d03bdfa8
commit 85a8a3a0ee
58 changed files with 883 additions and 129 deletions
@@ -0,0 +1,16 @@
Apache-2.0 License (Apache-2.0)
Copyright 2016 Dan Finlay
Copyright 2017-2019 @polkadot/ui-shared 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-shared/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-shared/demo.png)
@@ -0,0 +1,59 @@
// Copyright 2017-2019 @polkadot/ui-shared 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 { ColorGen } from './types';
import newSeeder from './seeder';
import newColors from './colors';
describe('colors', (): void => {
let colors: ColorGen;
beforeEach((): void => {
colors = newColors(newSeeder());
});
it('generates using default alpha', (): void => {
expect(
colors()
).toEqual(
// 'hsla(166.70000000000005, 98.6%, 27.6%, 0.9)'
'hsl(37.19999999999999, 100%, 54.9%)'
);
});
it('applies specified alpha', (): void => {
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', (): void => {
colors();
expect(
colors()
).not.toEqual('hsla(166.70000000000005, 98.6%, 27.6%, 0.9)');
});
it('works in edge conditions (0xff)', (): void => {
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,27 @@
// Copyright 2016 Dan Finlay
// Copyright 2017-2019 @polkadot/ui-shared 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 { ColorGen, Seeder } from './types';
import Color from 'color';
import { COLORS } from './defaults';
const WOBBLE = 30;
export default function colors (seeder: Seeder): ColorGen {
const amount = (seeder() * WOBBLE) - (WOBBLE / 2);
const all = COLORS.map((hex): Color =>
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,45 @@
// Copyright 2017-2019 @polkadot/ui-shared 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', (): void => {
it('applies default styles', (): void => {
expect(
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(container(100).style as any)._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', (): void => {
expect(
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(container(50, 'black', '', { display: 'block' }).style as any)._values
).toMatchObject({
background: 'black',
'border-radius': '25px',
display: 'block',
height: '50px',
margin: '0px',
overflow: 'hidden',
padding: '0px',
width: '50px'
});
});
it('applies the specified className', (): void => {
expect(
container(100, 'blue', 'testClass').className
).toEqual('testClass');
});
});
@@ -0,0 +1,27 @@
// Copyright 2016 Dan Finlay
// Copyright 2017-2019 @polkadot/ui-shared 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: unknown): void => {
element.style[key as number] = style[key as number];
});
return element;
}
@@ -0,0 +1,16 @@
// Copyright 2016 Dan Finlay
// Copyright 2017-2019 @polkadot/ui-shared 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: string[] = [
// 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,35 @@
// Copyright 2016 Dan Finlay
// Copyright 2017-2019 @polkadot/ui-shared 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 { isNull } from '@polkadot/util';
import { encodeAddress, randomAsU8a } from '@polkadot/util-crypto';
import identicon from '.';
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,29 @@
// Copyright 2017-2019 @polkadot/ui-shared 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 '.';
describe('identicon', (): void => {
it('generates a basic [0,..,0] identicon', (): void => {
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="background: hsl(37.19999999999999, 100%, 54%); 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', (): void => {
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="background: hsl(37.19999999999999, 100%, 54%); 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-2019 @polkadot/ui-shared 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 | Uint8Array, 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,29 @@
// Copyright 2017-2019 @polkadot/ui-shared 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 newSeeder from './seeder';
describe('seeder', (): void => {
let seeder: Seeder;
beforeEach((): void => {
seeder = newSeeder(new Uint8Array([1, 2, 3, 4]));
});
it('generates numbers using 2 spaces', (): void => {
expect(
seeder()
).toEqual(0.0156402587890625);
});
it('generates numbers using 2 spaces (incremented)', (): void => {
seeder();
expect(
seeder()
).toEqual(0.0078582763671875);
});
});
@@ -0,0 +1,31 @@
// Copyright 2017-2019 @polkadot/ui-shared 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 = (): number => {
index += 1;
if (index === seed.length) {
index = 0;
}
return seed[index];
};
return (): number => {
return ((next() * 256) + next()) / DIVISOR;
};
}
@@ -0,0 +1,18 @@
// Copyright 2017-2019 @polkadot/ui-shared 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', (): void => {
it('creates a circle shape', (): void => {
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-2019 @polkadot/ui-shared 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-2019 @polkadot/ui-shared 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-2019 @polkadot/ui-shared 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', (): void => {
it('creates a basic SVG circle element', (): void => {
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-2019 @polkadot/ui-shared 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-2019 @polkadot/ui-shared 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', (): void => {
it('creates a basic SVG element', (): void => {
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-2019 @polkadot/ui-shared 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-2019 @polkadot/ui-shared 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', (): void => {
it('creates a basic SVG rect element', (): void => {
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-2019 @polkadot/ui-shared 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-2019 @polkadot/ui-shared 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', (): void => {
it('creates a basic SVG element', (): void => {
expect(
xmlserializer.serializeToString(
svg('rect')
)
).toEqual('<rect xmlns="http://www.w3.org/2000/svg"/>');
});
});
@@ -0,0 +1,10 @@
// Copyright 2016 Dan Finlay
// Copyright 2017-2019 @polkadot/ui-shared 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,9 @@
// Copyright 2017-2019 @polkadot/ui-shared 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;
export interface ColorGen {
(alpha?: number): string;
}
@@ -0,0 +1,5 @@
// Copyright 2017-2019 @polkadot/ui-shared authors & contributors
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
declare module 'xmlserializer';
+4 -1
View File
@@ -1,5 +1,5 @@
// Copyright 2018 Paritytech via paritytech/oo7/polkadot-identicon
// Copyright 2018 @polkadot/react-identicon authors & contributors
// Copyright 2018 @polkadot/ui-shared authors & contributors
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
@@ -131,6 +131,9 @@ function getColors (address: string): string[] {
);
}
/**
* @description Generate a array of the circles that make up an indenticon
*/
export default function generate (address: string, isSixPoint: boolean = false): Circle[] {
const colors = getColors(address);