@versatiles/style 3.5.1 → 3.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.MD +126 -59
- package/dist/index.d.ts +9 -4
- package/dist/index.js +13 -3
- package/dist/index.test.js +50 -25
- package/dist/lib/decorator.d.ts +1 -2
- package/dist/lib/decorator.js +4 -0
- package/dist/lib/decorator.test.js +2 -2
- package/dist/lib/random_color.d.ts +9 -0
- package/dist/lib/random_color.js +150 -0
- package/dist/lib/random_color.test.d.ts +1 -0
- package/dist/lib/random_color.test.js +68 -0
- package/dist/lib/recolor.d.ts +3 -12
- package/dist/lib/recolor.js +2 -1
- package/dist/lib/recolor.test.d.ts +13 -1
- package/dist/lib/recolor.test.js +23 -10
- package/dist/lib/shortbread/layers.d.ts +2 -5
- package/dist/lib/shortbread/layers.js +14 -12
- package/dist/lib/shortbread/layers.test.js +1 -1
- package/dist/lib/shortbread/template.d.ts +2 -2
- package/dist/lib/shortbread/template.test.js +0 -2
- package/dist/lib/style_builder.d.ts +8 -28
- package/dist/lib/style_builder.js +60 -49
- package/dist/lib/style_builder.test.js +9 -17
- package/dist/lib/style_guesser.d.ts +2 -0
- package/dist/lib/style_guesser.js +123 -0
- package/dist/lib/style_guesser.test.d.ts +1 -0
- package/dist/lib/style_guesser.test.js +57 -0
- package/dist/lib/types.d.ts +86 -0
- package/dist/lib/types.js +89 -0
- package/dist/style/colorful.d.ts +5 -5
- package/dist/style/colorful.js +3 -3
- package/dist/style/graybeard.js +2 -2
- package/dist/style/neutrino.d.ts +5 -5
- package/dist/style/neutrino.js +3 -3
- package/package.json +19 -16
package/README.MD
CHANGED
|
@@ -7,91 +7,158 @@ Programatically generates StyleJSON for MapLibre.
|
|
|
7
7
|
|
|
8
8
|

|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
# Styles
|
|
11
11
|
|
|
12
12
|
* Colorful - colorful, full featured map
|
|
13
13
|
* Neutrino - light basemap
|
|
14
14
|
* Graybeard - gray basemap
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
# Download styles for versatiles.org
|
|
17
17
|
|
|
18
18
|
You can download the latest StyleJSONs from the [latest release](https://github.com/versatiles-org/versatiles-style/releases/latest/).
|
|
19
19
|
We provide each style with and without labels, and also in multiple languages.
|
|
20
20
|
|
|
21
21
|
Be aware that styles already include `tiles.versatiles.org` as source for tiles, fonts (glyphs) and icons (sprites). So you might want to update the URLs in the JSON.
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
Install `versatiles-style` via NPM:
|
|
26
|
-
```bash
|
|
27
|
-
npm install versatiles-style
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
Use it in Node.js:
|
|
31
|
-
```javascript
|
|
32
|
-
import { colorful } from 'versatiles-style';
|
|
33
|
-
let style = colorful({language:'de'});
|
|
34
|
-
writeFileSync('style.json', JSON.stringify(style));
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
## in a web browser
|
|
23
|
+
# Create styles in the frontend (web browser)
|
|
38
24
|
|
|
39
25
|
Download latest release:
|
|
26
|
+
|
|
40
27
|
```bash
|
|
41
28
|
wget "https://github.com/versatiles-org/versatiles-style/releases/latest/download/versatiles-style.js"
|
|
42
29
|
```
|
|
43
30
|
|
|
44
31
|
Use it in:
|
|
32
|
+
|
|
45
33
|
```html
|
|
46
34
|
<div id="map"></div>
|
|
47
35
|
<script src="maplibre-gl.js"></script>
|
|
48
36
|
<script src="versatiles-style.js"></script>
|
|
49
37
|
<script>
|
|
50
|
-
const
|
|
38
|
+
const styleBuilder = new VersaTilesStyle.Colorful();
|
|
39
|
+
styleBuilder.tilesUrl = ['tiles/{z}/{x}/{y}'];
|
|
40
|
+
|
|
51
41
|
const map = new maplibregl.Map({
|
|
52
42
|
container: 'map',
|
|
53
|
-
style:
|
|
54
|
-
glyphsUrl: baseUrl + 'fonts/{fontstack}/{range}.pbf',
|
|
55
|
-
spriteUrl: baseUrl + 'sprites/sprites',
|
|
56
|
-
tilesUrl: [baseUrl + 'tiles/{z}/{x}/{y}'],
|
|
57
|
-
})
|
|
43
|
+
style: styleBuilder.build()
|
|
58
44
|
});
|
|
59
45
|
<script>
|
|
60
46
|
```
|
|
61
47
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
`
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
48
|
+
# Create styles in the backend (Node.js)
|
|
49
|
+
|
|
50
|
+
Install `versatiles-style` via NPM:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
npm install versatiles-style
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Use it in Node.js:
|
|
57
|
+
|
|
58
|
+
```javascript
|
|
59
|
+
import { Colorful } from 'versatiles-style';
|
|
60
|
+
let style = new Colorful();
|
|
61
|
+
style.language = 'de';
|
|
62
|
+
writeFileSync('style.json', JSON.stringify(style.build()));
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
# API
|
|
66
|
+
|
|
67
|
+
<!--- This chapter is generated automatically --->
|
|
68
|
+
|
|
69
|
+
## Interfaces
|
|
70
|
+
|
|
71
|
+
### Interface: `TileJSONSpecificationRaster`<a id="interface_tilejsonspecificationraster"></a>
|
|
72
|
+
|
|
73
|
+
```typescript
|
|
74
|
+
interface {
|
|
75
|
+
attribution?: string;
|
|
76
|
+
bounds?: [number, number, number, number];
|
|
77
|
+
center?: [number, number];
|
|
78
|
+
description?: string;
|
|
79
|
+
fillzoom?: number;
|
|
80
|
+
format: "avif" | "jpg" | "png" | "webp";
|
|
81
|
+
grids?: string[];
|
|
82
|
+
legend?: string;
|
|
83
|
+
maxzoom?: number;
|
|
84
|
+
minzoom?: number;
|
|
85
|
+
name?: string;
|
|
86
|
+
scheme?: "xyz" | "tms";
|
|
87
|
+
template?: string;
|
|
88
|
+
tilejson?: "3.0.0";
|
|
89
|
+
tiles: string[];
|
|
90
|
+
type: "raster";
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Interface: `TileJSONSpecificationVector`<a id="interface_tilejsonspecificationvector"></a>
|
|
95
|
+
|
|
96
|
+
```typescript
|
|
97
|
+
interface {
|
|
98
|
+
attribution?: string;
|
|
99
|
+
bounds?: [number, number, number, number];
|
|
100
|
+
center?: [number, number];
|
|
101
|
+
description?: string;
|
|
102
|
+
fillzoom?: number;
|
|
103
|
+
format: "pbf";
|
|
104
|
+
grids?: string[];
|
|
105
|
+
legend?: string;
|
|
106
|
+
maxzoom?: number;
|
|
107
|
+
minzoom?: number;
|
|
108
|
+
name?: string;
|
|
109
|
+
scheme?: "xyz" | "tms";
|
|
110
|
+
template?: string;
|
|
111
|
+
tilejson?: "3.0.0";
|
|
112
|
+
tiles: string[];
|
|
113
|
+
type: "vector";
|
|
114
|
+
vector_layers: VectorLayer[];
|
|
115
|
+
}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## Type Aliases
|
|
119
|
+
|
|
120
|
+
### Type: `TileJSONSpecification`<a id="type_tilejsonspecification"></a>
|
|
121
|
+
|
|
122
|
+
**Type:** <code>[TileJSONSpecificationRaster](#interface_tilejsonspecificationraster) | [TileJSONSpecificationVector](#interface_tilejsonspecificationvector)</code>
|
|
123
|
+
|
|
124
|
+
## Functions
|
|
125
|
+
|
|
126
|
+
### Method: `colorful(options)`
|
|
127
|
+
|
|
128
|
+
<sup><a href="https://github.com/versatiles-org/versatiles-style/blob/6f93c18/src/index.ts#L4">\[src]</a></sup>
|
|
129
|
+
|
|
130
|
+
**Parameters:**
|
|
131
|
+
|
|
132
|
+
* <code>options: StylemakerOptions<default></code> (optional)
|
|
133
|
+
|
|
134
|
+
**Returns:** <code>MaplibreStyle</code>
|
|
135
|
+
|
|
136
|
+
### Method: `graybeard(options)`
|
|
137
|
+
|
|
138
|
+
<sup><a href="https://github.com/versatiles-org/versatiles-style/blob/6f93c18/src/index.ts#L9">\[src]</a></sup>
|
|
139
|
+
|
|
140
|
+
**Parameters:**
|
|
141
|
+
|
|
142
|
+
* <code>options: StylemakerOptions<default></code> (optional)
|
|
143
|
+
|
|
144
|
+
**Returns:** <code>MaplibreStyle</code>
|
|
145
|
+
|
|
146
|
+
### Method: `guessStyle(spec)`
|
|
147
|
+
|
|
148
|
+
<sup><a href="https://github.com/versatiles-org/versatiles-style/blob/6f93c18/src/lib/style_guesser.ts#L12">\[src]</a></sup>
|
|
149
|
+
|
|
150
|
+
**Parameters:**
|
|
151
|
+
|
|
152
|
+
* <code>spec: [TileJSONSpecification](#type_tilejsonspecification)</code>
|
|
153
|
+
|
|
154
|
+
**Returns:** <code>MaplibreStyle</code>
|
|
155
|
+
|
|
156
|
+
### Method: `neutrino(options)`
|
|
157
|
+
|
|
158
|
+
<sup><a href="https://github.com/versatiles-org/versatiles-style/blob/6f93c18/src/index.ts#L14">\[src]</a></sup>
|
|
159
|
+
|
|
160
|
+
**Parameters:**
|
|
161
|
+
|
|
162
|
+
* <code>options: StylemakerOptions<default></code> (optional)
|
|
163
|
+
|
|
164
|
+
**Returns:** <code>MaplibreStyle</code>
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
export
|
|
4
|
-
|
|
1
|
+
import type { MaplibreStyle, StylemakerOptions } from './lib/types.js';
|
|
2
|
+
import Colorful from './style/colorful.js';
|
|
3
|
+
export declare function colorful(options?: StylemakerOptions<Colorful>): MaplibreStyle;
|
|
4
|
+
import Graybeard from './style/graybeard.js';
|
|
5
|
+
export declare function graybeard(options?: StylemakerOptions<Graybeard>): MaplibreStyle;
|
|
6
|
+
import Neutrino from './style/neutrino.js';
|
|
7
|
+
export declare function neutrino(options?: StylemakerOptions<Neutrino>): MaplibreStyle;
|
|
8
|
+
export type { TileJSONSpecification, TileJSONSpecificationRaster, TileJSONSpecificationVector } from './lib/types.js';
|
|
9
|
+
export { default as guessStyle } from './lib/style_guesser.js';
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,13 @@
|
|
|
1
|
-
|
|
2
|
-
export
|
|
3
|
-
|
|
1
|
+
import Colorful from './style/colorful.js';
|
|
2
|
+
export function colorful(options) {
|
|
3
|
+
return new Colorful().build(options);
|
|
4
|
+
}
|
|
5
|
+
import Graybeard from './style/graybeard.js';
|
|
6
|
+
export function graybeard(options) {
|
|
7
|
+
return new Graybeard().build(options);
|
|
8
|
+
}
|
|
9
|
+
import Neutrino from './style/neutrino.js';
|
|
10
|
+
export function neutrino(options) {
|
|
11
|
+
return new Neutrino().build(options);
|
|
12
|
+
}
|
|
13
|
+
export { default as guessStyle } from './lib/style_guesser.js';
|
package/dist/index.test.js
CHANGED
|
@@ -1,38 +1,63 @@
|
|
|
1
|
-
|
|
2
|
-
import * as
|
|
3
|
-
import StyleBuilder from './lib/style_builder.js';
|
|
1
|
+
/* eslint-disable @typescript-eslint/naming-convention */
|
|
2
|
+
import * as builders from './index.js';
|
|
4
3
|
describe('Style Builders', () => {
|
|
5
|
-
const
|
|
6
|
-
'Colorful',
|
|
7
|
-
'Graybeard',
|
|
8
|
-
'Neutrino',
|
|
4
|
+
const styles = [
|
|
5
|
+
{ name: 'Colorful', builder: builders.colorful },
|
|
6
|
+
{ name: 'Graybeard', builder: builders.graybeard },
|
|
7
|
+
{ name: 'Neutrino', builder: builders.neutrino },
|
|
9
8
|
];
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
});
|
|
15
|
-
Object.entries(builderClasses).forEach(([styleName, builderClass]) => {
|
|
16
|
-
it(`should create and test an instance of ${styleName}`, () => {
|
|
17
|
-
const builder = new builderClass();
|
|
18
|
-
expect(builder).toBeInstanceOf(StyleBuilder);
|
|
19
|
-
expect(typeof builder.name).toBe('string');
|
|
20
|
-
builder.baseUrl = 'https://example.org';
|
|
21
|
-
const style = builder.build();
|
|
9
|
+
styles.forEach(({ name, builder }) => {
|
|
10
|
+
it(`should create and test an instance of ${name}`, () => {
|
|
11
|
+
expect(typeof builder).toBe('function');
|
|
12
|
+
const style = builder({ baseUrl: 'https://example.org' });
|
|
22
13
|
expect(JSON.stringify(style).length).toBeGreaterThan(50000);
|
|
23
|
-
expect(style.name).toBe('versatiles-' +
|
|
14
|
+
expect(style.name).toBe('versatiles-' + name.toLowerCase());
|
|
24
15
|
expect(style.glyphs).toBe('https://example.org/assets/fonts/{fontstack}/{range}.pbf');
|
|
25
16
|
expect(style.sprite).toBe('https://example.org/assets/sprites/sprites');
|
|
26
17
|
expect(Object.keys(style.sources).join(',')).toBe('versatiles-shortbread');
|
|
27
|
-
// @ts-expect-error: Still Overwhelmed
|
|
28
18
|
expect(style.sources['versatiles-shortbread'].tiles).toEqual(['https://example.org/tiles/osm/{z}/{x}/{y}']);
|
|
29
19
|
});
|
|
30
20
|
});
|
|
31
21
|
});
|
|
32
22
|
describe('Colorful', () => {
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
23
|
+
const style = builders.colorful({
|
|
24
|
+
baseUrl: 'https://dev.null',
|
|
25
|
+
colors: { commercial: '#f00' },
|
|
26
|
+
});
|
|
37
27
|
expect(style.glyphs).toBe('https://dev.null/assets/fonts/{fontstack}/{range}.pbf');
|
|
28
|
+
const paint = style.layers.find(l => l.id === 'land-commercial')?.paint;
|
|
29
|
+
expect(paint).toBeDefined();
|
|
30
|
+
if (paint == null)
|
|
31
|
+
throw Error();
|
|
32
|
+
expect(paint).toHaveProperty('fill-color');
|
|
33
|
+
if (!('fill-color' in paint))
|
|
34
|
+
throw Error();
|
|
35
|
+
expect(paint['fill-color']).toBe('#ff0000');
|
|
36
|
+
});
|
|
37
|
+
describe('guessStyle', () => {
|
|
38
|
+
it('should build raster styles', () => {
|
|
39
|
+
const style = builders.guessStyle({
|
|
40
|
+
type: 'raster',
|
|
41
|
+
tiles: [],
|
|
42
|
+
format: 'avif',
|
|
43
|
+
});
|
|
44
|
+
expect(style).toStrictEqual({
|
|
45
|
+
layers: [{ id: 'raster', source: 'rasterSource', type: 'raster' }],
|
|
46
|
+
sources: { rasterSource: { format: 'avif', tilejson: '3.0.0', tiles: [], type: 'raster' } },
|
|
47
|
+
version: 8,
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
it('should build vector styles', () => {
|
|
51
|
+
const style = builders.guessStyle({
|
|
52
|
+
type: 'vector',
|
|
53
|
+
tiles: [],
|
|
54
|
+
format: 'pbf',
|
|
55
|
+
vector_layers: [],
|
|
56
|
+
});
|
|
57
|
+
expect(style).toStrictEqual({
|
|
58
|
+
layers: [{ id: 'background', paint: { 'background-color': '#fff' }, type: 'background' }],
|
|
59
|
+
sources: { vectorSource: { format: 'pbf', tilejson: '3.0.0', tiles: [], type: 'vector', 'vector_layers': [] } },
|
|
60
|
+
'version': 8,
|
|
61
|
+
});
|
|
62
|
+
});
|
|
38
63
|
});
|
package/dist/lib/decorator.d.ts
CHANGED
|
@@ -1,3 +1,2 @@
|
|
|
1
|
-
import type { MaplibreLayer } from './
|
|
2
|
-
import type { StyleRules } from './style_builder.js';
|
|
1
|
+
import type { MaplibreLayer, StyleRules } from './types.js';
|
|
3
2
|
export declare function decorate(layers: MaplibreLayer[], rules: StyleRules): MaplibreLayer[];
|
package/dist/lib/decorator.js
CHANGED
|
@@ -9,6 +9,8 @@ export function decorate(layers, rules) {
|
|
|
9
9
|
const layerStyles = new Map();
|
|
10
10
|
// Iterate through the generated layer style rules
|
|
11
11
|
Object.entries(rules).forEach(([idDef, layerStyle]) => {
|
|
12
|
+
if (layerStyle == null)
|
|
13
|
+
return;
|
|
12
14
|
// Expand any braces in IDs and filter them through a RegExp if necessary
|
|
13
15
|
const ids = expandBraces(idDef).flatMap(id => {
|
|
14
16
|
if (!id.includes('*'))
|
|
@@ -41,6 +43,8 @@ export function decorate(layers, rules) {
|
|
|
41
43
|
// Function to process each style attribute for the layer
|
|
42
44
|
function processStyling(layer, styleRule) {
|
|
43
45
|
for (const [ruleKeyCamelCase, ruleValue] of Object.entries(styleRule)) {
|
|
46
|
+
if (ruleValue == null)
|
|
47
|
+
continue;
|
|
44
48
|
// CamelCase to not-camel-case
|
|
45
49
|
const ruleKey = ruleKeyCamelCase.replace(/[A-Z]/g, c => '-' + c.toLowerCase());
|
|
46
50
|
const propertyDefs = maplibreProperties.get(layer.type + '/' + ruleKey);
|
|
@@ -2,8 +2,8 @@ import { decorate } from './decorator.js';
|
|
|
2
2
|
import Color from 'color';
|
|
3
3
|
describe('decorate function', () => {
|
|
4
4
|
const mockLayers = [
|
|
5
|
-
{ id: 'layer1', type: 'fill', layout: {}, paint: {} },
|
|
6
|
-
{ id: 'layer2', type: 'line', layout: {}, paint: {} },
|
|
5
|
+
{ id: 'layer1', type: 'fill', layout: {}, paint: {}, source: 'versatiles-shortbread' },
|
|
6
|
+
{ id: 'layer2', type: 'line', layout: {}, paint: {}, source: 'versatiles-shortbread' },
|
|
7
7
|
];
|
|
8
8
|
const mockRules = {
|
|
9
9
|
'layer1': {
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export interface RandomColorOptions {
|
|
2
|
+
seed?: string;
|
|
3
|
+
hue?: number | string;
|
|
4
|
+
opacity?: number;
|
|
5
|
+
luminosity?: number | string;
|
|
6
|
+
saturation?: number | string;
|
|
7
|
+
}
|
|
8
|
+
export type RandomColorFunction = (options?: RandomColorOptions) => string;
|
|
9
|
+
export default function randomColorGenerator(startSeed?: number | string): RandomColorFunction;
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
export default function randomColorGenerator(startSeed) {
|
|
2
|
+
let seed = inputToSeed(startSeed);
|
|
3
|
+
const colorDictionary = initColorDictionary();
|
|
4
|
+
return randomColor;
|
|
5
|
+
function randomColor(options) {
|
|
6
|
+
options ??= {};
|
|
7
|
+
if (options.seed != null) {
|
|
8
|
+
seed = inputToSeed(options.seed);
|
|
9
|
+
}
|
|
10
|
+
options.opacity ??= 1;
|
|
11
|
+
const H = pickHue(options);
|
|
12
|
+
const S = pickSaturation(H, options);
|
|
13
|
+
const V = pickBrightness(H, S, options);
|
|
14
|
+
const hsl = HSVtoHSL([H, S, V]).map(v => v.toFixed(0));
|
|
15
|
+
if (options.opacity === 1) {
|
|
16
|
+
return `hsl(${hsl[0]},${hsl[1]}%,${hsl[2]}%)`;
|
|
17
|
+
}
|
|
18
|
+
else {
|
|
19
|
+
return `hsla(${hsl[0]},${hsl[1]}%,${hsl[2]}%,${options.opacity})`;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
function pickHue(options) {
|
|
23
|
+
let hue = randomWithin(getHueRange(options.hue));
|
|
24
|
+
if (hue < 0)
|
|
25
|
+
hue = 360 + hue;
|
|
26
|
+
return hue;
|
|
27
|
+
}
|
|
28
|
+
function pickSaturation(hue, options) {
|
|
29
|
+
if (options.hue === 'monochrome')
|
|
30
|
+
return 0;
|
|
31
|
+
if (options.luminosity === 'random')
|
|
32
|
+
return randomWithin([0, 100]);
|
|
33
|
+
const { saturationRange } = getColorInfo(hue);
|
|
34
|
+
let [sMin, sMax] = saturationRange;
|
|
35
|
+
if (options.saturation === 'strong')
|
|
36
|
+
return sMax;
|
|
37
|
+
switch (options.luminosity) {
|
|
38
|
+
case 'bright':
|
|
39
|
+
sMin = 55;
|
|
40
|
+
break;
|
|
41
|
+
case 'dark':
|
|
42
|
+
sMin = sMax - 10;
|
|
43
|
+
break;
|
|
44
|
+
case 'light':
|
|
45
|
+
sMax = 55;
|
|
46
|
+
break;
|
|
47
|
+
default:
|
|
48
|
+
}
|
|
49
|
+
return randomWithin([sMin, sMax]);
|
|
50
|
+
}
|
|
51
|
+
function pickBrightness(h, s, options) {
|
|
52
|
+
let bMin = getMinimumBrightness(h, s), bMax = 100;
|
|
53
|
+
switch (options.luminosity) {
|
|
54
|
+
case 'dark':
|
|
55
|
+
bMax = Math.min(100, bMin + 20);
|
|
56
|
+
break;
|
|
57
|
+
case 'light':
|
|
58
|
+
bMin = (bMax + bMin) / 2;
|
|
59
|
+
break;
|
|
60
|
+
case 'random':
|
|
61
|
+
bMin = 0;
|
|
62
|
+
bMax = 100;
|
|
63
|
+
break;
|
|
64
|
+
default:
|
|
65
|
+
}
|
|
66
|
+
return randomWithin([bMin, bMax]);
|
|
67
|
+
}
|
|
68
|
+
function getMinimumBrightness(h, s) {
|
|
69
|
+
const { lowerBounds } = getColorInfo(h);
|
|
70
|
+
for (let i = 0; i < lowerBounds.length - 1; i++) {
|
|
71
|
+
const [s1, v1] = lowerBounds[i];
|
|
72
|
+
const [s2, v2] = lowerBounds[i + 1];
|
|
73
|
+
if (s >= s1 && s <= s2) {
|
|
74
|
+
const m = (v2 - v1) / (s2 - s1), b = v1 - m * s1;
|
|
75
|
+
return m * s + b;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return 0;
|
|
79
|
+
}
|
|
80
|
+
function getHueRange(hue) {
|
|
81
|
+
if (typeof hue === 'number') {
|
|
82
|
+
if (hue < 360 && hue > 0)
|
|
83
|
+
return [hue, hue];
|
|
84
|
+
}
|
|
85
|
+
if (typeof hue === 'string') {
|
|
86
|
+
const color = colorDictionary[hue];
|
|
87
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
88
|
+
if (color?.hueRange)
|
|
89
|
+
return color.hueRange;
|
|
90
|
+
}
|
|
91
|
+
return [0, 360];
|
|
92
|
+
}
|
|
93
|
+
function getColorInfo(hue) {
|
|
94
|
+
// Maps red colors to make picking hue easier
|
|
95
|
+
if (hue >= 334 && hue <= 360)
|
|
96
|
+
hue -= 360;
|
|
97
|
+
for (const colorName in colorDictionary) {
|
|
98
|
+
const color = colorDictionary[colorName];
|
|
99
|
+
if (color.hueRange && hue >= color.hueRange[0] && hue <= color.hueRange[1]) {
|
|
100
|
+
return colorDictionary[colorName];
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
throw Error('Color not found');
|
|
104
|
+
}
|
|
105
|
+
function randomWithin(range) {
|
|
106
|
+
//Seeded random algorithm from http://indiegamr.com/generate-repeatable-random-numbers-in-js/
|
|
107
|
+
const max = range[1] || 1;
|
|
108
|
+
const min = range[0] || 0;
|
|
109
|
+
seed = (seed * 9301 + 49297) % 233280;
|
|
110
|
+
const rnd = seed / 233280.0;
|
|
111
|
+
return Math.floor(min + rnd * (max - min));
|
|
112
|
+
}
|
|
113
|
+
function initColorDictionary() {
|
|
114
|
+
const dic = {};
|
|
115
|
+
const defineColor = (name, hueRange, lowerBounds) => {
|
|
116
|
+
const [greyest] = lowerBounds;
|
|
117
|
+
const colorful = lowerBounds[lowerBounds.length - 1];
|
|
118
|
+
dic[name] = {
|
|
119
|
+
hueRange,
|
|
120
|
+
lowerBounds,
|
|
121
|
+
saturationRange: [greyest[0], colorful[0]],
|
|
122
|
+
brightnessRange: [colorful[1], greyest[1]],
|
|
123
|
+
};
|
|
124
|
+
};
|
|
125
|
+
defineColor('monochrome', null, [[0, 0], [100, 0]]);
|
|
126
|
+
defineColor('red', [-26, 18], [[20, 100], [30, 92], [40, 89], [50, 85], [60, 78], [70, 70], [80, 60], [90, 55], [100, 50]]);
|
|
127
|
+
defineColor('orange', [18, 46], [[20, 100], [30, 93], [40, 88], [50, 86], [60, 85], [70, 70], [100, 70]]);
|
|
128
|
+
defineColor('yellow', [46, 62], [[25, 100], [40, 94], [50, 89], [60, 86], [70, 84], [80, 82], [90, 80], [100, 75]]);
|
|
129
|
+
defineColor('green', [62, 178], [[30, 100], [40, 90], [50, 85], [60, 81], [70, 74], [80, 64], [90, 50], [100, 40]]);
|
|
130
|
+
defineColor('blue', [178, 257], [[20, 100], [30, 86], [40, 80], [50, 74], [60, 60], [70, 52], [80, 44], [90, 39], [100, 35]]);
|
|
131
|
+
defineColor('purple', [257, 282], [[20, 100], [30, 87], [40, 79], [50, 70], [60, 65], [70, 59], [80, 52], [90, 45], [100, 42]]);
|
|
132
|
+
defineColor('pink', [282, 334], [[20, 100], [30, 90], [40, 86], [60, 84], [80, 80], [90, 75], [100, 73]]);
|
|
133
|
+
return dic;
|
|
134
|
+
}
|
|
135
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
136
|
+
function HSVtoHSL(hsv) {
|
|
137
|
+
const s = hsv[1] / 100, v = hsv[2] / 100, k = (2 - s) * v;
|
|
138
|
+
return [hsv[0], 100 * s * v / (k < 1 ? k : 2 - k), 100 * k / 2];
|
|
139
|
+
}
|
|
140
|
+
function inputToSeed(input) {
|
|
141
|
+
if (input == null)
|
|
142
|
+
return 0;
|
|
143
|
+
if (typeof input === 'number')
|
|
144
|
+
return input;
|
|
145
|
+
let i = 0;
|
|
146
|
+
for (let p = 0; p < input.length; p++)
|
|
147
|
+
i = (i * 0x101 + input.charCodeAt(p)) % 0x100000000;
|
|
148
|
+
return i;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import randomColorGenerator from './random_color.js';
|
|
2
|
+
describe('RandomColor', () => {
|
|
3
|
+
let randomColor;
|
|
4
|
+
beforeEach(() => {
|
|
5
|
+
randomColor = randomColorGenerator();
|
|
6
|
+
});
|
|
7
|
+
test('constructor initializes without errors', () => {
|
|
8
|
+
expect(randomColor).toBeDefined();
|
|
9
|
+
});
|
|
10
|
+
describe('randomColor method', () => {
|
|
11
|
+
test('returns a valid color string', () => {
|
|
12
|
+
const color = randomColor();
|
|
13
|
+
expect(isValidHSLA(color)).toBeTruthy();
|
|
14
|
+
});
|
|
15
|
+
test('returns correct color string for some test cases', () => {
|
|
16
|
+
expect(randomColor({ hue: 'red' })).toBe('hsl(343,65%,51%)');
|
|
17
|
+
expect(randomColor({ hue: 120 })).toBe('hsl(120,77%,32%)');
|
|
18
|
+
expect(randomColor({ luminosity: 'dark' })).toBe('hsl(135,96%,31%)');
|
|
19
|
+
expect(randomColor({ saturation: 'strong' })).toBe('hsl(193,100%,24%)');
|
|
20
|
+
expect(randomColor({ opacity: 0.5 })).toBe('hsla(242,55%,42%,0.5)');
|
|
21
|
+
expect(randomColor({ seed: 'testSeed' })).toBe('hsl(185,90%,23%)');
|
|
22
|
+
});
|
|
23
|
+
test('consistent color generation with a seed', () => {
|
|
24
|
+
const options = { seed: 'consistentSeed' };
|
|
25
|
+
const color1 = randomColor(options);
|
|
26
|
+
const color2 = randomColor(options);
|
|
27
|
+
expect(color1).toBe(color2);
|
|
28
|
+
});
|
|
29
|
+
test('different color generation without a seed', () => {
|
|
30
|
+
const color1 = randomColor();
|
|
31
|
+
const color2 = randomColor();
|
|
32
|
+
expect(color1).not.toBe(color2);
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
function isValidHSLA(textColor) {
|
|
37
|
+
let match, h, s, l, a;
|
|
38
|
+
if (textColor.startsWith('hsla')) {
|
|
39
|
+
match = /^hsla\((\d+),(\d+)%,(\d+)%,([.0-9]+)\)$/.exec(textColor);
|
|
40
|
+
if (match == null)
|
|
41
|
+
return false;
|
|
42
|
+
[, h, s, l, a] = match;
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
match = /^hsl\((\d+),(\d+)%,(\d+)%\)$/.exec(textColor);
|
|
46
|
+
if (match == null)
|
|
47
|
+
return false;
|
|
48
|
+
[, h, s, l] = match;
|
|
49
|
+
a = '1';
|
|
50
|
+
}
|
|
51
|
+
if (!check(h, 0, 360))
|
|
52
|
+
return false;
|
|
53
|
+
if (!check(s, 0, 100))
|
|
54
|
+
return false;
|
|
55
|
+
if (!check(l, 0, 100))
|
|
56
|
+
return false;
|
|
57
|
+
if (!check(a, 0, 1))
|
|
58
|
+
return false;
|
|
59
|
+
return true;
|
|
60
|
+
function check(textNumber, min, max) {
|
|
61
|
+
const value = parseFloat(textNumber);
|
|
62
|
+
if (value < min)
|
|
63
|
+
return false;
|
|
64
|
+
if (value > max)
|
|
65
|
+
return false;
|
|
66
|
+
return true;
|
|
67
|
+
}
|
|
68
|
+
}
|
package/dist/lib/recolor.d.ts
CHANGED
|
@@ -1,13 +1,4 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
3
|
-
invert?: boolean;
|
|
4
|
-
rotate?: number;
|
|
5
|
-
saturate?: number;
|
|
6
|
-
gamma?: number;
|
|
7
|
-
contrast?: number;
|
|
8
|
-
brightness?: number;
|
|
9
|
-
tint?: number;
|
|
10
|
-
tintColor?: string;
|
|
11
|
-
}
|
|
1
|
+
import type { RecolorOptions, StylemakerColors } from './types.js';
|
|
2
|
+
import type StyleBuilder from './style_builder.ts';
|
|
12
3
|
export declare function getDefaultRecolorFlags(): RecolorOptions;
|
|
13
|
-
export declare function recolor(colors:
|
|
4
|
+
export declare function recolor<Subclass extends StyleBuilder<Subclass>>(colors: StylemakerColors<Subclass>, opt?: RecolorOptions): void;
|
package/dist/lib/recolor.js
CHANGED
|
@@ -29,7 +29,8 @@ export function recolor(colors, opt) {
|
|
|
29
29
|
if ((opt.tint !== undefined) && (opt.tintColor !== undefined) && (opt.tint !== 0))
|
|
30
30
|
tint(opt.tint, Color(opt.tintColor));
|
|
31
31
|
function forEachColor(callback) {
|
|
32
|
-
|
|
32
|
+
for (const k in colors)
|
|
33
|
+
colors[k] = callback(colors[k]);
|
|
33
34
|
}
|
|
34
35
|
function invert() {
|
|
35
36
|
forEachColor(c => c.negate());
|
|
@@ -1 +1,13 @@
|
|
|
1
|
-
|
|
1
|
+
import type { StyleRules, StyleRulesOptions } from './types.js';
|
|
2
|
+
import StyleBuilder from './style_builder.js';
|
|
3
|
+
export default class TestStyle extends StyleBuilder<TestStyle> {
|
|
4
|
+
readonly name: string;
|
|
5
|
+
defaultFonts: {};
|
|
6
|
+
defaultColors: {
|
|
7
|
+
c0: string;
|
|
8
|
+
c1: string;
|
|
9
|
+
c2: string;
|
|
10
|
+
c3: string;
|
|
11
|
+
};
|
|
12
|
+
protected getStyleRules(_options: StyleRulesOptions<TestStyle>): StyleRules;
|
|
13
|
+
}
|