gridviz 3.0.41 → 3.1.1
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 +18 -15
- package/dist/gridviz.js +3227 -1941
- package/dist/gridviz.min.js +1 -1
- package/package.json +6 -4
- package/src/button/Button.js +77 -68
- package/src/button/FullscreenButton.js +123 -123
- package/src/button/ZoomButtons.js +109 -104
- package/src/core/Drawable.js +25 -3
- package/src/core/GeoCanvas.js +15 -0
- package/src/core/Legend.js +267 -245
- package/src/core/Map.js +180 -30
- package/src/core/Tooltip.js +191 -185
- package/src/dataset/TiledGrid.js +74 -24
- package/src/index.js +82 -79
- package/src/layer/GridLayer.js +196 -194
- package/src/layer/LabelLayer.js +7 -4
- package/src/legend/ColorCategoryLegend.js +127 -125
- package/src/legend/ColorDiscreteLegend.js +116 -114
- package/src/legend/ColorLegend.js +33 -11
- package/src/legend/OrientationLegend.js +94 -92
- package/src/legend/SizeLegend.js +8 -1
- package/src/legend/TernaryLegend.js +317 -314
- package/src/legend/VennLegend.js +202 -0
- package/src/style/CircleColorSizeWebGLStyle.js +249 -0
- package/src/style/CompositionStyle.js +1 -1
- package/src/style/DotDensityStyle.js +8 -4
- package/src/style/ImageStyle.js +1 -1
- package/src/style/Interpolator.js +31 -6
- package/src/style/IsoFenceStyle.js +1 -1
- package/src/style/LegoStyle.js +6 -5
- package/src/style/MosaicStyle.js +1 -1
- package/src/style/NinjaStarStyle.js +1 -1
- package/src/style/PillarStyle.js +1 -1
- package/src/style/RandomWalkInterpolator.js +213 -0
- package/src/style/ShadingRayStyle.js +16 -2
- package/src/style/ShadingStyle.js +80 -72
- package/src/style/ShapeColorSizeStyle.js +18 -2
- package/src/style/SideCategoryStyle.js +1 -1
- package/src/style/SideStyle.js +1 -1
- package/src/style/SideTanakaStyle.js +1 -1
- package/src/style/SquareColorCategoryWebGLStyle.js +71 -20
- package/src/style/SquareColorWebGLStyle.js +7 -4
- package/src/style/StrokeStyle.js +1 -1
- package/src/style/TextStyle.js +1 -1
- package/src/style/TimeSeriesStyle.js +14 -7
- package/src/utils/WebGLSquareColoringAdvanced.js +47 -9
- package/src/utils/scale.js +20 -0
- package/src/utils/webGLUtils.js +52 -0
package/src/core/Drawable.js
CHANGED
|
@@ -1,6 +1,19 @@
|
|
|
1
1
|
//@ts-check
|
|
2
2
|
'use strict'
|
|
3
3
|
|
|
4
|
+
/**
|
|
5
|
+
* Tells if an element (a layer or a style) is visible at a zoom level: its visible property is a function of the zoom
|
|
6
|
+
* level, or a boolean; it is visible when not specified. Works for any object, whether it is a Drawable or not.
|
|
7
|
+
*
|
|
8
|
+
* @param {{visible?: (function(number):boolean)|boolean}} element
|
|
9
|
+
* @param {number} z The zoom level.
|
|
10
|
+
* @returns {boolean}
|
|
11
|
+
*/
|
|
12
|
+
export function isVisibleAt(element, z) {
|
|
13
|
+
const v = element.visible
|
|
14
|
+
return typeof v === 'function' ? !!v(z) : v !== false
|
|
15
|
+
}
|
|
16
|
+
|
|
4
17
|
/**
|
|
5
18
|
* This is an abstract class used to group elements shared between Layer and Style classes.
|
|
6
19
|
*
|
|
@@ -15,9 +28,9 @@ export class Drawable {
|
|
|
15
28
|
constructor(opts) {
|
|
16
29
|
opts = opts || {}
|
|
17
30
|
|
|
18
|
-
/**
|
|
19
|
-
*
|
|
20
|
-
* @type {function(number):boolean} */
|
|
31
|
+
/** Specifies if the element should be visible or not: a function of the zoom level, or a boolean
|
|
32
|
+
* (to show or hide the element, then call map.redraw()). Visible when not specified.
|
|
33
|
+
* @type {(function(number):boolean)|boolean|undefined} */
|
|
21
34
|
this.visible = opts.visible
|
|
22
35
|
|
|
23
36
|
/** A function returning the alpha (transparency/opacity), between 0.0 (fully transparent) and 1.0 (fully opaque).
|
|
@@ -38,6 +51,15 @@ export class Drawable {
|
|
|
38
51
|
this.filterBlendOperation = opts.filterBlendOperation // (z) => "multiply"
|
|
39
52
|
}
|
|
40
53
|
|
|
54
|
+
/**
|
|
55
|
+
* Tells if the element is visible at a zoom level.
|
|
56
|
+
* @param {number} z The zoom level.
|
|
57
|
+
* @returns {boolean}
|
|
58
|
+
*/
|
|
59
|
+
isVisible(z) {
|
|
60
|
+
return isVisibleAt(this, z)
|
|
61
|
+
}
|
|
62
|
+
|
|
41
63
|
/**
|
|
42
64
|
* Draw filter.
|
|
43
65
|
*
|
package/src/core/GeoCanvas.js
CHANGED
|
@@ -343,6 +343,21 @@ export class GeoCanvas {
|
|
|
343
343
|
return [kx, 0.0, 0.0, 0.0, ky, 0.0, -kx * this.view.x, -ky * this.view.y, 1.0]
|
|
344
344
|
}
|
|
345
345
|
|
|
346
|
+
/**
|
|
347
|
+
* Get the transformation matrix to webGL screen coordinates, for positions given relative to the center of the view
|
|
348
|
+
* (x - view.x, y - view.y).
|
|
349
|
+
*
|
|
350
|
+
* The GPU computes with 32 bits floats, which have 7 digits: with the geographical coordinates of a map (millions of
|
|
351
|
+
* meters), the translation of getWebGLTransform() loses the position of the squares when zoomed in, by tens of pixels
|
|
352
|
+
* (the squares are not where the cells are). Positions relative to the center of the view are small numbers: no loss.
|
|
353
|
+
* The relative positions are computed in JavaScript, with 64 bits floats.
|
|
354
|
+
*/
|
|
355
|
+
getWebGLTransformCentered() {
|
|
356
|
+
const kx = 2.0 / (this.w * this.view.z)
|
|
357
|
+
const ky = 2.0 / (this.h * this.view.z)
|
|
358
|
+
return [kx, 0.0, 0.0, 0.0, ky, 0.0, 0.0, 0.0, 1.0]
|
|
359
|
+
}
|
|
360
|
+
|
|
346
361
|
/** The function specifying how to draw the map. */
|
|
347
362
|
redraw() {
|
|
348
363
|
throw new Error('Method redraw not implemented.')
|
package/src/core/Legend.js
CHANGED
|
@@ -1,245 +1,267 @@
|
|
|
1
|
-
//@ts-check
|
|
2
|
-
'use strict'
|
|
3
|
-
|
|
4
|
-
import { select } from 'd3-selection'
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* A legend container.
|
|
8
|
-
*
|
|
9
|
-
* @module core
|
|
10
|
-
* @author Joseph Davies, Julien Gaffuri
|
|
11
|
-
*/
|
|
12
|
-
export class Legend {
|
|
13
|
-
/**
|
|
14
|
-
* @param {Object} opts
|
|
15
|
-
*/
|
|
16
|
-
constructor(opts) {
|
|
17
|
-
opts = opts || {}
|
|
18
|
-
|
|
19
|
-
/** @type {string} */
|
|
20
|
-
this.id = opts.id
|
|
21
|
-
|
|
22
|
-
//TODO stop using it. Use style method below instead.
|
|
23
|
-
|
|
24
|
-
/** @type {number} @deprecated */
|
|
25
|
-
this.top = opts.top
|
|
26
|
-
/** @type {number} @deprecated */
|
|
27
|
-
this.bottom = opts.bottom
|
|
28
|
-
/** @type {number} @deprecated */
|
|
29
|
-
this.left = opts.left
|
|
30
|
-
/** @type {number} @deprecated */
|
|
31
|
-
this.right = opts.right
|
|
32
|
-
/** @type {string} @deprecated */
|
|
33
|
-
this.background = opts.background || 'none'
|
|
34
|
-
/** @type {string} @deprecated */
|
|
35
|
-
this.padding = opts.padding || '5px'
|
|
36
|
-
/** @type {string} @deprecated */
|
|
37
|
-
this.border = opts.border || '0px'
|
|
38
|
-
/** @type {string} @deprecated */
|
|
39
|
-
this['border-radius'] = opts['border-radius'] || 'none'
|
|
40
|
-
/** @type {string} @deprecated */
|
|
41
|
-
this['box-shadow'] = opts['box-shadow'] || 'none'
|
|
42
|
-
/** @type {string} @deprecated */
|
|
43
|
-
this['font-family'] = opts['font-family'] || 'Helvetica, Arial, sans-serif
|
|
44
|
-
/** @type {string} @deprecated */
|
|
45
|
-
//this.width = opts.width
|
|
46
|
-
/** @type {string} @deprecated */
|
|
47
|
-
//this.height = opts.height
|
|
48
|
-
|
|
49
|
-
//the div element
|
|
50
|
-
if (this.id) this.div = select('#' + this.id)
|
|
51
|
-
|
|
52
|
-
if (!this.div || this.div.empty()) {
|
|
53
|
-
this.div = select(document.createElement('div'))
|
|
54
|
-
if (this.id) this.div.attr('id', this.id)
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// Which style options the caller set. The ones they didn't set are written as a CSS variable with the
|
|
58
|
-
// usual value as its fallback (--gridviz-legend-font-family, ...): nothing changes unless a page defines the variable.
|
|
59
|
-
/** @private */
|
|
60
|
-
this._set = {
|
|
61
|
-
padding: opts.padding !== undefined,
|
|
62
|
-
fontFamily: opts['font-family'] !== undefined,
|
|
63
|
-
titleFontSize: opts.titleFontSize !== undefined,
|
|
64
|
-
titleFontWeight: opts.titleFontWeight !== undefined,
|
|
65
|
-
labelFontSize: opts.labelFontSize !== undefined,
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
// CSS classes, to style legends from a stylesheet: gridviz-legend on every legend, a modifier for the
|
|
69
|
-
// type (see each legend class), and whatever the caller asks for with the className option.
|
|
70
|
-
this.div.classed('gridviz-legend', true)
|
|
71
|
-
if (opts.className)
|
|
72
|
-
for (const c of String(opts.className).split(/\s+/)) if (c) this.div.classed(c, true)
|
|
73
|
-
|
|
74
|
-
//set style
|
|
75
|
-
this.div.style('background', this.background)
|
|
76
|
-
this.div.style('padding', this._css('padding', this.padding, this._set.padding))
|
|
77
|
-
this.div.style('border', this.border)
|
|
78
|
-
this.div.style('border-radius', this['border-radius'])
|
|
79
|
-
this.div.style('box-shadow', this['box-shadow'])
|
|
80
|
-
this.div.style('font-family', this._css('font-family', this['font-family'], this._set.fontFamily))
|
|
81
|
-
|
|
82
|
-
//if (this.width) this.div.style('width', this.width)
|
|
83
|
-
//if (this.height) this.div.style('height', this.height)
|
|
84
|
-
|
|
85
|
-
//title
|
|
86
|
-
this.title = opts.title
|
|
87
|
-
this.titleFontSize = opts.titleFontSize || '
|
|
88
|
-
this.titleFontWeight = opts.titleFontWeight || '
|
|
89
|
-
|
|
90
|
-
//label
|
|
91
|
-
this.labelFontSize = opts.labelFontSize || '
|
|
92
|
-
this.labelUnitText = opts.labelUnitText || ''
|
|
93
|
-
this.labelFormat = opts.labelFormat
|
|
94
|
-
|
|
95
|
-
/**
|
|
96
|
-
* Whether the title and the labels are HTML (to italicise a word, for example) instead of plain text.
|
|
97
|
-
* Off by default, which is plain text. Only turn it on for text you trust: it is not escaped.
|
|
98
|
-
* @type {boolean} */
|
|
99
|
-
this.labelsAsHTML = opts.labelsAsHTML === true
|
|
100
|
-
|
|
101
|
-
/**
|
|
102
|
-
* Whether hovering a swatch of the legend shows only the matching cells on the map.
|
|
103
|
-
* @type {boolean} */
|
|
104
|
-
this.interactive = opts.interactive !== false
|
|
105
|
-
|
|
106
|
-
/**
|
|
107
|
-
* The style the legend belongs to, and a function to redraw the map. Both set by the layer when it draws.
|
|
108
|
-
* @type {import('./Style.js').Style|undefined} */
|
|
109
|
-
this.style_ = undefined
|
|
110
|
-
/**
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
this.
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
*
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
this.
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
*
|
|
180
|
-
* @
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
*
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
*
|
|
228
|
-
* @param {string}
|
|
229
|
-
* @
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
1
|
+
//@ts-check
|
|
2
|
+
'use strict'
|
|
3
|
+
|
|
4
|
+
import { select } from 'd3-selection'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* A legend container.
|
|
8
|
+
*
|
|
9
|
+
* @module core
|
|
10
|
+
* @author Joseph Davies, Julien Gaffuri
|
|
11
|
+
*/
|
|
12
|
+
export class Legend {
|
|
13
|
+
/**
|
|
14
|
+
* @param {Object} opts
|
|
15
|
+
*/
|
|
16
|
+
constructor(opts) {
|
|
17
|
+
opts = opts || {}
|
|
18
|
+
|
|
19
|
+
/** @type {string} */
|
|
20
|
+
this.id = opts.id
|
|
21
|
+
|
|
22
|
+
//TODO stop using it. Use style method below instead.
|
|
23
|
+
|
|
24
|
+
/** @type {number} @deprecated */
|
|
25
|
+
this.top = opts.top
|
|
26
|
+
/** @type {number} @deprecated */
|
|
27
|
+
this.bottom = opts.bottom
|
|
28
|
+
/** @type {number} @deprecated */
|
|
29
|
+
this.left = opts.left
|
|
30
|
+
/** @type {number} @deprecated */
|
|
31
|
+
this.right = opts.right
|
|
32
|
+
/** @type {string} @deprecated */
|
|
33
|
+
this.background = opts.background || 'none'
|
|
34
|
+
/** @type {string} @deprecated */
|
|
35
|
+
this.padding = opts.padding || '5px'
|
|
36
|
+
/** @type {string} @deprecated */
|
|
37
|
+
this.border = opts.border || '0px'
|
|
38
|
+
/** @type {string} @deprecated */
|
|
39
|
+
this['border-radius'] = opts['border-radius'] || 'none'
|
|
40
|
+
/** @type {string} @deprecated */
|
|
41
|
+
this['box-shadow'] = opts['box-shadow'] || 'none'
|
|
42
|
+
/** @type {string} @deprecated */
|
|
43
|
+
this['font-family'] = opts['font-family'] || "'Helvetica Neue', Arial, Helvetica, sans-serif"
|
|
44
|
+
/** @type {string} @deprecated */
|
|
45
|
+
//this.width = opts.width
|
|
46
|
+
/** @type {string} @deprecated */
|
|
47
|
+
//this.height = opts.height
|
|
48
|
+
|
|
49
|
+
//the div element
|
|
50
|
+
if (this.id) this.div = select('#' + this.id)
|
|
51
|
+
|
|
52
|
+
if (!this.div || this.div.empty()) {
|
|
53
|
+
this.div = select(document.createElement('div'))
|
|
54
|
+
if (this.id) this.div.attr('id', this.id)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Which style options the caller set. The ones they didn't set are written as a CSS variable with the
|
|
58
|
+
// usual value as its fallback (--gridviz-legend-font-family, ...): nothing changes unless a page defines the variable.
|
|
59
|
+
/** @private */
|
|
60
|
+
this._set = {
|
|
61
|
+
padding: opts.padding !== undefined,
|
|
62
|
+
fontFamily: opts['font-family'] !== undefined,
|
|
63
|
+
titleFontSize: opts.titleFontSize !== undefined,
|
|
64
|
+
titleFontWeight: opts.titleFontWeight !== undefined,
|
|
65
|
+
labelFontSize: opts.labelFontSize !== undefined,
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// CSS classes, to style legends from a stylesheet: gridviz-legend on every legend, a modifier for the
|
|
69
|
+
// type (see each legend class), and whatever the caller asks for with the className option.
|
|
70
|
+
this.div.classed('gridviz-legend', true)
|
|
71
|
+
if (opts.className)
|
|
72
|
+
for (const c of String(opts.className).split(/\s+/)) if (c) this.div.classed(c, true)
|
|
73
|
+
|
|
74
|
+
//set style
|
|
75
|
+
this.div.style('background', this.background)
|
|
76
|
+
this.div.style('padding', this._css('padding', this.padding, this._set.padding))
|
|
77
|
+
this.div.style('border', this.border)
|
|
78
|
+
this.div.style('border-radius', this['border-radius'])
|
|
79
|
+
this.div.style('box-shadow', this['box-shadow'])
|
|
80
|
+
this.div.style('font-family', this._css('font-family', this['font-family'], this._set.fontFamily))
|
|
81
|
+
|
|
82
|
+
//if (this.width) this.div.style('width', this.width)
|
|
83
|
+
//if (this.height) this.div.style('height', this.height)
|
|
84
|
+
|
|
85
|
+
//title
|
|
86
|
+
this.title = opts.title
|
|
87
|
+
this.titleFontSize = opts.titleFontSize || '15px'
|
|
88
|
+
this.titleFontWeight = opts.titleFontWeight || '500'
|
|
89
|
+
|
|
90
|
+
//label
|
|
91
|
+
this.labelFontSize = opts.labelFontSize || '14px'
|
|
92
|
+
this.labelUnitText = opts.labelUnitText || ''
|
|
93
|
+
this.labelFormat = opts.labelFormat
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Whether the title and the labels are HTML (to italicise a word, for example) instead of plain text.
|
|
97
|
+
* Off by default, which is plain text. Only turn it on for text you trust: it is not escaped.
|
|
98
|
+
* @type {boolean} */
|
|
99
|
+
this.labelsAsHTML = opts.labelsAsHTML === true
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Whether hovering a swatch of the legend shows only the matching cells on the map.
|
|
103
|
+
* @type {boolean} */
|
|
104
|
+
this.interactive = opts.interactive !== false
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* The style the legend belongs to, and a function to redraw the map. Both set by the layer when it draws.
|
|
108
|
+
* @type {import('./Style.js').Style|undefined} */
|
|
109
|
+
this.style_ = undefined
|
|
110
|
+
/**
|
|
111
|
+
* All the styles of the layer the legend is in, set by the layer when it draws.
|
|
112
|
+
* @type {Array.<import('./Style.js').Style>|undefined} */
|
|
113
|
+
this.layerStyles = undefined
|
|
114
|
+
/** @type {function|undefined} */
|
|
115
|
+
this.redraw = undefined
|
|
116
|
+
/**
|
|
117
|
+
* The element the legends are drawn in, set by the layer when it draws.
|
|
118
|
+
* @type {HTMLElement|undefined} */
|
|
119
|
+
this.container = undefined
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Draw at the width of the container instead of this.width (width:'fill').
|
|
123
|
+
* @type {boolean} @protected */
|
|
124
|
+
this.fill = opts.width === 'fill'
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Show only the cells drawn in some colours on the map, or all of them again.
|
|
129
|
+
* @param {Array.<string>|null} colors
|
|
130
|
+
*/
|
|
131
|
+
highlight(colors) {
|
|
132
|
+
const styles = this.highlightedStyles()
|
|
133
|
+
if (!this.interactive || !styles.length) return
|
|
134
|
+
const before = styles.some((s) => s.highlightColors !== null)
|
|
135
|
+
// flag the legend while hovered, so that the map's redraw leaves its element in place:
|
|
136
|
+
// a hovered element that is detached and appended again never gets its mouseleave
|
|
137
|
+
if (this.div) this.div.attr('data-gridviz-hovered', colors && colors.length ? '' : null)
|
|
138
|
+
for (const s of styles) s.setHighlight(colors)
|
|
139
|
+
// nothing to redraw when the highlight didn't change (mouse moving between a swatch's parts)
|
|
140
|
+
if (!before && styles.every((s) => s.highlightColors === null)) return
|
|
141
|
+
if (this.redraw) this.redraw()
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* The styles whose cells are highlighted when a swatch is hovered: the style of the legend, by default.
|
|
146
|
+
* A legend of several styles (a Venn diagram, for example) gives more.
|
|
147
|
+
* @returns {Array.<import('./Style.js').Style>}
|
|
148
|
+
* @protected
|
|
149
|
+
*/
|
|
150
|
+
highlightedStyles() {
|
|
151
|
+
return this.style_ ? [this.style_] : []
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Make a swatch react to the mouse: hovering it highlights the cells of a colour on the map.
|
|
156
|
+
* @param {*} selection The d3 selection of the swatch (any element)
|
|
157
|
+
* @param {string} color The colour the swatch stands for
|
|
158
|
+
* @param {{enter?:function, leave?:function}} [extra] Extra feedback on the swatch itself
|
|
159
|
+
*/
|
|
160
|
+
bindHover(selection, color, extra) {
|
|
161
|
+
if (!this.interactive) return
|
|
162
|
+
selection
|
|
163
|
+
.style('cursor', 'pointer')
|
|
164
|
+
.on('mouseenter', () => {
|
|
165
|
+
if (extra && extra.enter) extra.enter()
|
|
166
|
+
this.highlight([color])
|
|
167
|
+
})
|
|
168
|
+
.on('mouseleave', () => {
|
|
169
|
+
if (extra && extra.leave) extra.leave()
|
|
170
|
+
this.highlight(null)
|
|
171
|
+
})
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* A style value with a CSS variable around it, unless the caller set the option themselves.
|
|
176
|
+
* @param {string} name The variable's name, after --gridviz-legend-
|
|
177
|
+
* @param {string} value The value to use when the variable isn't defined
|
|
178
|
+
* @param {boolean} custom
|
|
179
|
+
* @returns {string}
|
|
180
|
+
* @protected
|
|
181
|
+
*/
|
|
182
|
+
_css(name, value, custom) {
|
|
183
|
+
return custom ? value : `var(--gridviz-legend-${name}, ${value})`
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/** The font size of the labels, for the style() of a label element.
|
|
187
|
+
* @returns {string}
|
|
188
|
+
* @protected */
|
|
189
|
+
labelFontSizeCSS() {
|
|
190
|
+
return this._css('label-font-size', this.labelFontSize, this._set.labelFontSize)
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/** The color of the labels (CSS variable --gridviz-legend-label-color), for the style() of a label element, as 'fill' for the text of a svg and 'color' for a html element.
|
|
194
|
+
* @returns {string}
|
|
195
|
+
* @protected */
|
|
196
|
+
labelColorCSS() {
|
|
197
|
+
return 'var(--gridviz-legend-label-color, #333)'
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Mark the root element as a type of legend: the CSS class gridviz-legend--<type>.
|
|
202
|
+
* @param {string} type
|
|
203
|
+
* @protected
|
|
204
|
+
*/
|
|
205
|
+
setType(type) {
|
|
206
|
+
this.div.classed('gridviz-legend--' + type, true)
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* The width to draw at: this.width, or with width:'fill' the width of the container the legends are in
|
|
211
|
+
* (the legendContainer of the map), less this legend's own padding.
|
|
212
|
+
* @param {number} fallback Used when the container has no width (yet, or hidden)
|
|
213
|
+
* @returns {number}
|
|
214
|
+
* @protected
|
|
215
|
+
*/
|
|
216
|
+
availableWidth(fallback) {
|
|
217
|
+
if (!this.fill) return this.width
|
|
218
|
+
const container = this.container
|
|
219
|
+
const w = container && container.clientWidth
|
|
220
|
+
if (!w) return fallback
|
|
221
|
+
const p = parseFloat(this.padding) || 0
|
|
222
|
+
return Math.max(60, Math.floor(w - 2 * p))
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Set the text of a d3 selection: as HTML if the legend has labelsAsHTML, as plain text otherwise.
|
|
227
|
+
* @param {*} selection
|
|
228
|
+
* @param {string} text
|
|
229
|
+
* @protected
|
|
230
|
+
*/
|
|
231
|
+
setText(selection, text) {
|
|
232
|
+
return this.labelsAsHTML ? selection.html(text) : selection.text(text)
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
makeTitle() {
|
|
236
|
+
if (!this.title) return
|
|
237
|
+
this.setText(
|
|
238
|
+
this.div
|
|
239
|
+
.append('div')
|
|
240
|
+
.classed('gridviz-legend-title', true)
|
|
241
|
+
.style('font-size', this._css('title-font-size', this.titleFontSize, this._set.titleFontSize))
|
|
242
|
+
.style('font-weight', this._css('title-font-weight', this.titleFontWeight, this._set.titleFontWeight))
|
|
243
|
+
.style('margin-bottom', '7px'),
|
|
244
|
+
this.title
|
|
245
|
+
)
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Apply a style to the legend div.
|
|
250
|
+
* @param {string} k
|
|
251
|
+
* @param {string} v
|
|
252
|
+
* @returns {this|string}
|
|
253
|
+
*/
|
|
254
|
+
style(k, v) {
|
|
255
|
+
if (arguments.length == 1) return this.div.style(k)
|
|
256
|
+
this.div.style(k, v)
|
|
257
|
+
return this
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* @param {Object} opts
|
|
262
|
+
* @abstract
|
|
263
|
+
*/
|
|
264
|
+
update(opts = {}) {
|
|
265
|
+
console.error('Legend update not implemented yet.')
|
|
266
|
+
}
|
|
267
|
+
}
|