gridviz 2.1.13 → 2.1.15

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.
Files changed (62) hide show
  1. package/README.md +124 -125
  2. package/dist/gridviz.js +8900 -8368
  3. package/dist/gridviz.min.js +1 -1
  4. package/package.json +77 -77
  5. package/src/App.js +760 -758
  6. package/src/BackgroundLayer.js +182 -182
  7. package/src/BackgroundLayerWMS.js +131 -131
  8. package/src/Dataset.js +76 -76
  9. package/src/DatasetComponent.js +113 -113
  10. package/src/GeoCanvas.js +420 -420
  11. package/src/LabelLayer.js +190 -190
  12. package/src/Layer.js +103 -103
  13. package/src/Legend.js +86 -86
  14. package/src/LineLayer.js +150 -150
  15. package/src/Style.js +131 -129
  16. package/src/Tooltip.js +182 -180
  17. package/src/button/Button.js +66 -54
  18. package/src/button/FullscreenButton.js +120 -98
  19. package/src/button/ZoomButtons.js +95 -72
  20. package/src/dataset/COGeo.js +24 -24
  21. package/src/dataset/CSVGrid.js +104 -104
  22. package/src/dataset/GeoTIFF.js +127 -127
  23. package/src/dataset/GridTile.js +41 -41
  24. package/src/dataset/LGrid.js +54 -54
  25. package/src/dataset/TiledGrid.js +264 -264
  26. package/src/index.js +73 -71
  27. package/src/legend/ColorCategoryLegend.js +124 -124
  28. package/src/legend/ColorDiscreteLegend.js +103 -103
  29. package/src/legend/ColorLegend.js +128 -128
  30. package/src/legend/SegmentOrientationLegend.js +87 -87
  31. package/src/legend/SegmentWidthLegend.js +101 -101
  32. package/src/legend/SizeLegend.js +141 -141
  33. package/src/style/CompositionStyle.js +300 -300
  34. package/src/style/CompositionStyle_.js +293 -0
  35. package/src/style/ContourStyle.js +54 -54
  36. package/src/style/DotDensityStyle.js +156 -156
  37. package/src/style/IsoFenceStyle.js +262 -262
  38. package/src/style/JoyPlotStyle.js +135 -135
  39. package/src/style/LegoStyle.js +112 -112
  40. package/src/style/MosaicStyle.js +107 -107
  41. package/src/style/NinjaStarStyle.js +125 -125
  42. package/src/style/PillarStyle.js +230 -230
  43. package/src/style/SegmentStyle.js +142 -142
  44. package/src/style/ShapeColorSizeStyle.js +152 -152
  45. package/src/style/ShapeColorSizeStyle_.js +108 -0
  46. package/src/style/SideCatStyle.js +172 -172
  47. package/src/style/SideStyle.js +216 -216
  48. package/src/style/SquareColorCatWGLStyle.js +120 -120
  49. package/src/style/SquareColorWGLStyle.js +146 -146
  50. package/src/style/StrokeStyle.js +131 -131
  51. package/src/style/TanakaStyle.js +160 -160
  52. package/src/style/TextStyle.js +132 -132
  53. package/src/style/TimeSeriesStyle.js +259 -259
  54. package/src/utils/Utils.js +35 -35
  55. package/src/utils/WebGLRectangleColoring.js +106 -106
  56. package/src/utils/WebGLSquareColoring.js +118 -118
  57. package/src/utils/WebGLSquareColoringAdvanced.js +230 -230
  58. package/src/utils/WebGLSquareColoringCatAdvanced.js +133 -133
  59. package/src/utils/WebGLSquareColoringKS.js +153 -153
  60. package/src/utils/stretching.js +154 -131
  61. package/src/utils/webGLUtils.js +71 -71
  62. package/src/utils/zoomUtils.js +142 -142
package/src/Style.js CHANGED
@@ -1,129 +1,131 @@
1
- //@ts-check
2
- 'use strict'
3
-
4
- /**
5
- * Statistics of a set of values
6
- * @typedef {{min:number,max:number}} Stat */
7
-
8
- /** @typedef {"square"|"circle"|"diamond"|"donut"|"none"} Shape */
9
-
10
- /**
11
- * A style, to show a grid dataset.
12
- *
13
- * @abstract
14
- *
15
- * @author Joseph Davies, Julien Gaffuri
16
- */
17
- export class Style {
18
- /**
19
- * @abstract
20
- * @param {{filter?:function(import('./Dataset').Cell):boolean,offset?:function(import('./Dataset').Cell,number,number):{dx:number,dy:number},visible?:boolean,alpha?:function(number):number,blendOperation?:function(number):GlobalCompositeOperation,minZoom?:number,maxZoom?:number}} opts
21
- * minZoom: The minimum zoom level when to show the layer. maxZoom: The maximum zoom level when to show the layer
22
- */
23
- constructor(opts) {
24
- opts = opts || {}
25
-
26
- /** A filter function to apply to the cell list, to filter out some cells not to be drawn (such as for example the cells with value=0).
27
- * @protected
28
- * @type {function(import('./Dataset').Cell):boolean} */
29
- this.filter = opts.filter || (() => true)
30
-
31
- /** An offset. This is to alter the position of all symbols in a given direction. In geographical unit.
32
- * @protected
33
- * @type {function(import('./Dataset').Cell,number,number):{dx:number,dy:number}} */
34
- this.offset = opts.offset || ((c, r, zf) => ({ dx: 0, dy: 0 }))
35
-
36
- /** An attribute to specify if a style should be drawn or not
37
- * @type {boolean} */
38
- this.visible = opts.visible === false ? false : true
39
-
40
- /** A function returning the alpha (transparency/opacity), between 0.0 (fully transparent) and 1.0 (fully opaque).
41
- * The function parameter is the zoom factor.
42
- * (see CanvasRenderingContext2D: globalAlpha property)
43
- * @type {function(number):number|undefined} */
44
- this.alpha = opts.alpha
45
-
46
- /** A function returning the blend operation. The function parameter is the zoom factor.
47
- * (see CanvasRenderingContext2D: globalCompositeOperation property)
48
- * @type {function(number):GlobalCompositeOperation} */
49
- this.blendOperation = opts.blendOperation || (zf => "source-over")
50
-
51
- /** The minimum zoom factor: Below this level, the layer is not shown.
52
- * @type {number}
53
- * */
54
- this.minZoom = opts.minZoom || 0
55
-
56
- /** The maximum zoom factor: Above this level, the layer is not shown.
57
- * @type {number}
58
- * */
59
- this.maxZoom = opts.maxZoom || Infinity
60
-
61
- //ensure acceptable values for the zoom limits.
62
- if (this.minZoom >= this.maxZoom)
63
- throw new Error('Unexpected zoom limits for layer. Zoom min should be smaller than zoom max.')
64
-
65
- /**
66
- * @public
67
- * @type {Array.<import("./Legend").Legend>} */
68
- this.legends = []
69
- }
70
-
71
- /**
72
- * Draw cells.
73
- *
74
- * @param {Array.<import('./Dataset').Cell>} cells The cells to draw.
75
- * @param {number} resolution Their resolution (in geographic unit)
76
- * @param {import("./GeoCanvas").GeoCanvas} cg The canvas where to draw them.
77
- * @abstract
78
- */
79
- draw(cells, resolution, cg) {
80
- throw new Error('Method draw not implemented.')
81
- }
82
-
83
- //getters and setters
84
-
85
- /** @returns {function(import('./Dataset').Cell,number,number):{dx:number,dy:number}} */
86
- getOffset() {
87
- return this.offset
88
- }
89
- /** @param {function(import('./Dataset').Cell,number,number):{dx:number,dy:number}} val @returns {this} */
90
- setOffset(val) {
91
- this.offset = val
92
- return this
93
- }
94
-
95
- /** Hide all legend elements of the style, if any
96
- * @param {object} opts
97
- * @returns {this} */
98
- updateLegends(opts) {
99
- for (const lg of this.legends) lg.update(opts)
100
- return this
101
- }
102
-
103
- /**
104
- * Compute some statistics on a value of some cells.
105
- * This is used to define how to draw specifically the cells within the view.
106
- * TODO: compute median ?
107
- *
108
- * @param {Array.<import('./Dataset').Cell>} cells
109
- * @param {function(import('./Dataset').Cell):number} valFun
110
- * @param {boolean} ignoreZeros
111
- * @returns {Stat | undefined}
112
- */
113
- static getStatistics(cells, valFun, ignoreZeros) {
114
- if (!cells || cells.length == 0) return undefined
115
- let min = Infinity
116
- let max = -Infinity
117
- //let sum = 0
118
- //let nb = 0
119
- for (const cell of cells) {
120
- const v = +valFun(cell)
121
- if (ignoreZeros && !v) continue
122
- if (v < min) min = v
123
- if (v > max) max = v
124
- //sum += v
125
- //nb++
126
- }
127
- return { min: min, max: max }
128
- }
129
- }
1
+ //@ts-check
2
+ 'use strict'
3
+
4
+ /**
5
+ * Statistics of a set of values
6
+ * @typedef {{min:number,max:number}} Stat */
7
+
8
+ /** @typedef {"square"|"circle"|"diamond"|"donut"|"none"} Shape */
9
+
10
+ /**
11
+ * A style, to show a grid dataset.
12
+ *
13
+ * @author Joseph Davies, Julien Gaffuri
14
+ */
15
+ export class Style {
16
+ /**
17
+ * @abstract
18
+ * @param {{filter?:function(import('./Dataset').Cell):boolean,offset?:function(import('./Dataset').Cell,number,number):{dx:number,dy:number},visible?:boolean,alpha?:function(number):number,blendOperation?:function(number):GlobalCompositeOperation,minZoom?:number,maxZoom?:number,drawFun?:function}} opts
19
+ */
20
+ constructor(opts) {
21
+ opts = opts || {}
22
+
23
+ /** A filter function to apply to the cell list, to filter out some cells not to be drawn (such as for example the cells with value=0).
24
+ * @protected
25
+ * @type {function(import('./Dataset').Cell):boolean} */
26
+ this.filter = opts.filter || (() => true)
27
+
28
+ /** An offset. This is to alter the position of all symbols in a given direction. In geographical unit.
29
+ * @protected
30
+ * @type {function(import('./Dataset').Cell,number,number):{dx:number,dy:number}} */
31
+ this.offset = opts.offset || ((c, r, zf) => ({ dx: 0, dy: 0 }))
32
+
33
+ /** An attribute to specify if a style should be drawn or not
34
+ * @type {boolean} */
35
+ this.visible = opts.visible === false ? false : true
36
+
37
+ /** A function returning the alpha (transparency/opacity), between 0.0 (fully transparent) and 1.0 (fully opaque).
38
+ * The function parameter is the zoom factor.
39
+ * (see CanvasRenderingContext2D: globalAlpha property)
40
+ * @type {function(number):number|undefined} */
41
+ this.alpha = opts.alpha
42
+
43
+ /** A function returning the blend operation. The function parameter is the zoom factor.
44
+ * (see CanvasRenderingContext2D: globalCompositeOperation property)
45
+ * @type {function(number):GlobalCompositeOperation} */
46
+ this.blendOperation = opts.blendOperation || (zf => "source-over")
47
+
48
+ /** The minimum zoom factor: Below this level, the layer is not shown.
49
+ * @type {number}
50
+ * */
51
+ this.minZoom = opts.minZoom || 0
52
+
53
+ /** The maximum zoom factor: Above this level, the layer is not shown.
54
+ * @type {number}
55
+ * */
56
+ this.maxZoom = opts.maxZoom || Infinity
57
+
58
+ /** A draw function for the style.
59
+ * @type {function} */
60
+ this.drawFun = opts.drawFun
61
+
62
+ //ensure acceptable values for the zoom limits.
63
+ if (this.minZoom >= this.maxZoom)
64
+ throw new Error('Unexpected zoom limits for layer. Zoom min should be smaller than zoom max.')
65
+
66
+ /**
67
+ * @public
68
+ * @type {Array.<import("./Legend").Legend>} */
69
+ this.legends = []
70
+ }
71
+
72
+ /**
73
+ * Draw cells.
74
+ *
75
+ * @param {Array.<import('./Dataset').Cell>} cells The cells to draw.
76
+ * @param {number} resolution Their resolution (in geographic unit)
77
+ * @param {import("./GeoCanvas").GeoCanvas} cg The canvas where to draw them.
78
+ * @abstract
79
+ */
80
+ draw(cells, resolution, cg) {
81
+ if (this.drawFun) this.drawFun(cells, resolution, cg)
82
+ else throw new Error('Method draw not implemented.')
83
+ }
84
+
85
+ //getters and setters
86
+
87
+ /** @returns {function(import('./Dataset').Cell,number,number):{dx:number,dy:number}} */
88
+ getOffset() {
89
+ return this.offset
90
+ }
91
+ /** @param {function(import('./Dataset').Cell,number,number):{dx:number,dy:number}} val @returns {this} */
92
+ setOffset(val) {
93
+ this.offset = val
94
+ return this
95
+ }
96
+
97
+ /** Hide all legend elements of the style, if any
98
+ * @param {object} opts
99
+ * @returns {this} */
100
+ updateLegends(opts) {
101
+ for (const lg of this.legends) lg.update(opts)
102
+ return this
103
+ }
104
+
105
+ /**
106
+ * Compute some statistics on a value of some cells.
107
+ * This is used to define how to draw specifically the cells within the view.
108
+ * TODO: compute median ?
109
+ *
110
+ * @param {Array.<import('./Dataset').Cell>} cells
111
+ * @param {function(import('./Dataset').Cell):number} valFun
112
+ * @param {boolean} ignoreZeros
113
+ * @returns {Stat | undefined}
114
+ */
115
+ static getStatistics(cells, valFun, ignoreZeros) {
116
+ if (!cells || cells.length == 0) return undefined
117
+ let min = Infinity
118
+ let max = -Infinity
119
+ //let sum = 0
120
+ //let nb = 0
121
+ for (const cell of cells) {
122
+ const v = +valFun(cell)
123
+ if (ignoreZeros && !v) continue
124
+ if (v < min) min = v
125
+ if (v > max) max = v
126
+ //sum += v
127
+ //nb++
128
+ }
129
+ return { min: min, max: max }
130
+ }
131
+ }
package/src/Tooltip.js CHANGED
@@ -1,180 +1,182 @@
1
- //@ts-check
2
- 'use strict'
3
-
4
- import { select } from 'd3-selection'
5
- //import { transition } from "d3-transition";
6
-
7
- /**
8
- * A generic class to make a tooltip.
9
- * It is a div element, which can be moved under the mouse pointer and filled with some information in html.
10
- */
11
- export class Tooltip {
12
- /**
13
- * @param {object} opts
14
- */
15
- constructor(opts) {
16
- opts = opts || {}
17
-
18
- /** @type {string} */
19
- this.div = opts.div || 'tooltip_eurostat'
20
- /** @type {string} */
21
- this.maxWidth = opts.maxWidth || '20em'
22
- /** @type {string} */
23
- this.fontSize = opts.fontSize || '1.2em'
24
- /** @type {string} */
25
- this.background = opts.background || 'white'
26
- /** @type {string} */
27
- this.padding = opts.padding || '5px'
28
- /** @type {string} */
29
- this.border = opts.border || '0px'
30
- /** @type {string} */
31
- this['border-radius'] = opts['border-radius'] || '5px'
32
- /** @type {string} */
33
- this['box-shadow'] = opts['box-shadow'] || '5px 5px 5px grey'
34
- /** @type {string} */
35
- this['font-family'] = opts['font-family'] || 'Helvetica, Arial, sans-serif'
36
-
37
- /** @type {number} */
38
- this.transitionDuration = opts.transitionDuration || 100
39
- /** @type {number} */
40
- this.xOffset = opts.xOffset || 30
41
- /** @type {number} */
42
- this.yOffset = opts.yOffset || 20
43
- /** @type {number} */ // e.g. to prevent mouse cursor covering cell being highlighted
44
- this.yMouseOffset = opts.yMouseOffset || 0
45
- /** @type {number} */
46
- this.xMouseOffset = opts.xMouseOffset || 0
47
- /** @type {HTMLElement} */
48
- this.parentElement = opts.parentElement || document.body
49
-
50
- /**
51
- * @public
52
- * @type {import("d3-selection").Selection} */
53
- this.tooltip = select('#' + this.div)
54
- if (this.tooltip.empty())
55
- this.tooltip = select(
56
- '#' + this.parentElement.id && this.parentElement.id != ''
57
- ? '#' + this.parentElement.id
58
- : 'body'
59
- )
60
- .append('div')
61
- .attr('id', this.div)
62
-
63
- //initialise
64
- this.tooltip.style('max-width', this.maxWidth)
65
- this.tooltip.style('overflow', 'hidden')
66
- this.tooltip.style('font-size', this.fontSize)
67
- this.tooltip.style('background', this.background)
68
- this.tooltip.style('padding', this.padding)
69
- this.tooltip.style('border', this.border)
70
- this.tooltip.style('border-radius', this['border-radius'])
71
- this.tooltip.style('box-shadow', this['box-shadow'])
72
- this.tooltip.style('font-family', this['font-family'])
73
- this.tooltip.style('position', 'absolute')
74
- this.tooltip.style('pointer-events', 'none')
75
- this.tooltip.style('opacity', '0')
76
- this.tooltip.style('text-wrap', 'nowrap')
77
-
78
- // aria-labels (thanks to wahlatlas)
79
- this.tooltip.attr('role', 'tooltip').attr('aria-live', 'polite')
80
- }
81
-
82
- /** Show the tooltip */
83
- show() {
84
- // @ts-ignore
85
- this.tooltip.transition().duration(this.transitionDuration).style('opacity', 1)
86
- }
87
-
88
- /** Hide the tooltip */
89
- hide() {
90
- // @ts-ignore
91
- this.tooltip.transition().duration(this.transitionDuration).style('opacity', 0)
92
- }
93
-
94
- /**
95
- * Set the content of the tooltip.
96
- * @param {string} html
97
- */
98
- html(html) {
99
- this.tooltip.html(html)
100
- }
101
-
102
- /**
103
- * Set the position of the tooltip at the mouse event position.
104
- * @param {MouseEvent} event
105
- */
106
- setPosition(event) {
107
- let parentRect = this.parentElement.getBoundingClientRect()
108
-
109
- let x = event.pageX + this.xOffset
110
- let y = event.pageY - this.yOffset
111
-
112
- this.tooltip.style('left', x + 'px').style('top', y + 'px')
113
-
114
- this.ensureTooltipInsideContainer(event, parentRect)
115
- }
116
-
117
- /*
118
- my.mouseover = function (event, html) {
119
- if (html) my.html(html);
120
- my.setPosition(event);
121
- my.show()
122
- //this.ensureTooltipInsideContainer();
123
- };
124
-
125
- my.mousemove = function (event) {
126
- my.setPosition(event);
127
- //this.ensureTooltipInsideContainer();
128
- };
129
-
130
- my.mouseout = function () {
131
- my.hide();
132
- };*/
133
-
134
- style(k, v) {
135
- if (arguments.length == 1) return this.tooltip.style(k)
136
- this.tooltip.style(k, v)
137
- return this
138
- }
139
-
140
- attr(k, v) {
141
- if (arguments.length == 1) return this.tooltip.attr(k)
142
- this.tooltip.attr(k, v)
143
- return this
144
- }
145
-
146
- /**
147
- * @function ensureTooltipInsideContainer
148
- * @description Prevents the tooltip from overflowing out of the App container (ensures that the tooltip is inside the gridviz container)
149
- * @param {MouseEvent} event
150
- * @param {DOMRect} parentRect
151
- */
152
- ensureTooltipInsideContainer = function (event, parentRect) {
153
- let node = this.tooltip.node()
154
- let parentWidth = parentRect.width
155
- let parentHeight = parentRect.height
156
-
157
- //too far right
158
- if (node.offsetLeft > parentRect.left + parentWidth - node.clientWidth) {
159
- let left = event.x - node.clientWidth - this.xOffset
160
- node.style.left = left + 'px'
161
- // check if mouse covers tooltip
162
- if (node.offsetLeft + node.clientWidth > event.x) {
163
- //move tooltip left so it doesnt cover mouse
164
- let left2 = event.x - node.clientWidth - this.xOffset
165
- node.style.left = left2 + 'px'
166
- }
167
- // node.style.top = node.offsetTop + config.yOffset + "px";
168
- }
169
-
170
- //too far down
171
- if (node.offsetTop + node.clientHeight > parentRect.top + parentHeight) {
172
- node.style.top = node.offsetTop - node.clientHeight + 'px'
173
- }
174
-
175
- //too far up
176
- if (node.offsetTop < parentRect.top) {
177
- node.style.top = parentRect.top + this.yOffset + 'px'
178
- }
179
- }
180
- }
1
+ //@ts-check
2
+ 'use strict'
3
+
4
+ import { select } from 'd3-selection'
5
+ //import { transition } from "d3-transition";
6
+
7
+ /**
8
+ * A generic class to make a tooltip.
9
+ * It is a div element, which can be moved under the mouse pointer and filled with some information in html.
10
+ */
11
+ export class Tooltip {
12
+ /**
13
+ * @param {object} opts
14
+ */
15
+ constructor(opts) {
16
+ opts = opts || {}
17
+
18
+ /** @type {string} */
19
+ this.div = opts.div || 'tooltip_eurostat'
20
+ /** @type {string} */
21
+ this.maxWidth = opts.maxWidth || '20em'
22
+ /** @type {string} */
23
+ this.fontSize = opts.fontSize || '1.2em'
24
+ /** @type {string} */
25
+ this.background = opts.background || 'white'
26
+ /** @type {string} */
27
+ this.padding = opts.padding || '5px'
28
+ /** @type {string} */
29
+ this.border = opts.border || '0px'
30
+ /** @type {string} */
31
+ this['border-radius'] = opts['border-radius'] || '0px'
32
+ /** @type {string} */
33
+ this['box-shadow'] = opts['box-shadow'] || '5px 5px 5px grey'
34
+ /** @type {string} */
35
+ this['font-family'] = opts['font-family'] || 'Helvetica, Arial, sans-serif'
36
+
37
+ /** @type {number} */
38
+ this.transitionDuration = opts.transitionDuration || 100
39
+ /** @type {number} */
40
+ this.xOffset = opts.xOffset || 30
41
+ /** @type {number} */
42
+ this.yOffset = opts.yOffset || 20
43
+ /** @type {number} */ // e.g. to prevent mouse cursor covering cell being highlighted
44
+ this.yMouseOffset = opts.yMouseOffset || 0
45
+ /** @type {number} */
46
+ this.xMouseOffset = opts.xMouseOffset || 0
47
+ /** @type {HTMLElement} */
48
+ this.parentElement = opts.parentElement || document.body
49
+
50
+ /**
51
+ * @public
52
+ * @type {import("d3-selection").Selection} */
53
+ this.tooltip = select('#' + this.div)
54
+
55
+ if (this.tooltip.empty()) {
56
+ //create tooltip DOM node
57
+ // this.tooltip = select(
58
+ // '#' + this.parentElement.id && this.parentElement.id != ''
59
+ // ? '#' + this.parentElement.id
60
+ // : 'body'
61
+ // )
62
+ this.tooltip = select('body').append('div').attr('id', this.div)
63
+ }
64
+
65
+ //initialise
66
+ this.tooltip.style('max-width', this.maxWidth)
67
+ this.tooltip.style('overflow', 'hidden')
68
+ this.tooltip.style('font-size', this.fontSize)
69
+ this.tooltip.style('background', this.background)
70
+ this.tooltip.style('padding', this.padding)
71
+ this.tooltip.style('border', this.border)
72
+ this.tooltip.style('border-radius', this['border-radius'])
73
+ this.tooltip.style('box-shadow', this['box-shadow'])
74
+ this.tooltip.style('font-family', this['font-family'])
75
+ this.tooltip.style('position', 'absolute')
76
+ this.tooltip.style('pointer-events', 'none')
77
+ this.tooltip.style('opacity', '0')
78
+ this.tooltip.style('text-wrap', 'nowrap')
79
+
80
+ // aria-labels (thanks to wahlatlas)
81
+ this.tooltip.attr('role', 'tooltip').attr('aria-live', 'polite')
82
+ }
83
+
84
+ /** Show the tooltip */
85
+ show() {
86
+ // @ts-ignore
87
+ this.tooltip.transition().duration(this.transitionDuration).style('opacity', 1)
88
+ }
89
+
90
+ /** Hide the tooltip */
91
+ hide() {
92
+ // @ts-ignore
93
+ this.tooltip.transition().duration(this.transitionDuration).style('opacity', 0)
94
+ }
95
+
96
+ /**
97
+ * Set the content of the tooltip.
98
+ * @param {string} html
99
+ */
100
+ html(html) {
101
+ this.tooltip.html(html)
102
+ }
103
+
104
+ /**
105
+ * Set the position of the tooltip at the mouse event position.
106
+ * @param {MouseEvent} event
107
+ */
108
+ setPosition(event) {
109
+ let parentRect = this.parentElement.getBoundingClientRect()
110
+
111
+ let x = event.pageX + this.xOffset
112
+ let y = event.pageY - this.yOffset
113
+
114
+ this.tooltip.style('left', x + 'px').style('top', y + 'px')
115
+
116
+ this.ensureTooltipInsideContainer(event, parentRect)
117
+ }
118
+
119
+ /*
120
+ my.mouseover = function (event, html) {
121
+ if (html) my.html(html);
122
+ my.setPosition(event);
123
+ my.show()
124
+ //this.ensureTooltipInsideContainer();
125
+ };
126
+
127
+ my.mousemove = function (event) {
128
+ my.setPosition(event);
129
+ //this.ensureTooltipInsideContainer();
130
+ };
131
+
132
+ my.mouseout = function () {
133
+ my.hide();
134
+ };*/
135
+
136
+ style(k, v) {
137
+ if (arguments.length == 1) return this.tooltip.style(k)
138
+ this.tooltip.style(k, v)
139
+ return this
140
+ }
141
+
142
+ attr(k, v) {
143
+ if (arguments.length == 1) return this.tooltip.attr(k)
144
+ this.tooltip.attr(k, v)
145
+ return this
146
+ }
147
+
148
+ /**
149
+ * @function ensureTooltipInsideContainer
150
+ * @description Prevents the tooltip from overflowing out of the App container (ensures that the tooltip is inside the gridviz container)
151
+ * @param {MouseEvent} event
152
+ * @param {DOMRect} parentRect
153
+ */
154
+ ensureTooltipInsideContainer = function (event, parentRect) {
155
+ let node = this.tooltip.node()
156
+ let parentWidth = parentRect.width
157
+ let parentHeight = parentRect.height
158
+
159
+ //too far right
160
+ if (node.offsetLeft > parentRect.left + parentWidth - node.clientWidth) {
161
+ let left = event.x - node.clientWidth - this.xOffset
162
+ node.style.left = left + 'px'
163
+ // check if mouse covers tooltip
164
+ if (node.offsetLeft + node.clientWidth > event.x) {
165
+ //move tooltip left so it doesnt cover mouse
166
+ let left2 = event.x - node.clientWidth - this.xOffset
167
+ node.style.left = left2 + 'px'
168
+ }
169
+ // node.style.top = node.offsetTop + config.yOffset + "px";
170
+ }
171
+
172
+ //too far down
173
+ if (node.offsetTop + node.clientHeight > parentRect.top + parentHeight) {
174
+ node.style.top = node.offsetTop - node.clientHeight + 'px'
175
+ }
176
+
177
+ //too far up
178
+ if (node.offsetTop < parentRect.top) {
179
+ node.style.top = parentRect.top + this.yOffset + 'px'
180
+ }
181
+ }
182
+ }