gridviz 3.0.17 → 3.0.18
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/dist/gridviz.js +113 -96
- package/dist/gridviz.min.js +1 -1
- package/package.json +1 -1
- package/src/button/Button.js +1 -1
- package/src/core/GeoCanvas.js +21 -11
- package/src/core/Map.js +41 -32
- package/src/core/Tooltip.js +49 -51
package/package.json
CHANGED
package/src/button/Button.js
CHANGED
|
@@ -46,7 +46,7 @@ export class Button {
|
|
|
46
46
|
this.style('display', 'flex')
|
|
47
47
|
this.style('justify-content', 'center')
|
|
48
48
|
this.style('align-items', 'center')
|
|
49
|
-
this.style('width', '
|
|
49
|
+
this.style('width', '35px')
|
|
50
50
|
this.style('height', '30px')
|
|
51
51
|
// this.style(padding , '4px'
|
|
52
52
|
|
package/src/core/GeoCanvas.js
CHANGED
|
@@ -87,24 +87,29 @@ export class GeoCanvas {
|
|
|
87
87
|
.on('zoom', (e) => {
|
|
88
88
|
const t = e.transform
|
|
89
89
|
const f = tP.k / t.k
|
|
90
|
-
|
|
91
|
-
|
|
90
|
+
|
|
91
|
+
// Get container's bounding rect to adjust for offsets
|
|
92
|
+
const containerRect = this.canvas.getBoundingClientRect()
|
|
93
|
+
|
|
94
|
+
// Adjust for container's offset and zoom center
|
|
95
|
+
const offsetX = e.sourceEvent.offsetX - containerRect.left
|
|
96
|
+
const offsetY = e.sourceEvent.offsetY - containerRect.top
|
|
97
|
+
|
|
98
|
+
if (f === 1) {
|
|
99
|
+
// Pan logic
|
|
92
100
|
const dx = tP.x - t.x
|
|
93
101
|
const dy = tP.y - t.y
|
|
94
102
|
this.pan(dx * this.view.z, -dy * this.view.z)
|
|
95
103
|
} else {
|
|
96
104
|
const se = e.sourceEvent
|
|
105
|
+
|
|
97
106
|
if (se instanceof WheelEvent) {
|
|
98
|
-
//
|
|
99
|
-
this.zoom(
|
|
100
|
-
f,
|
|
101
|
-
this.pixToGeoX(e.sourceEvent.offsetX),
|
|
102
|
-
this.pixToGeoY(e.sourceEvent.offsetY)
|
|
103
|
-
)
|
|
107
|
+
// Zoom at mouse position, adjusted by container offset
|
|
108
|
+
this.zoom(f, this.pixToGeoX(offsetX), this.pixToGeoY(offsetY))
|
|
104
109
|
} else if (se instanceof TouchEvent) {
|
|
105
110
|
if (!se.targetTouches.length) return
|
|
106
|
-
|
|
107
|
-
//
|
|
111
|
+
|
|
112
|
+
// Compute average position of the touches
|
|
108
113
|
let tx = 0,
|
|
109
114
|
ty = 0
|
|
110
115
|
for (let tt of se.targetTouches) {
|
|
@@ -113,7 +118,12 @@ export class GeoCanvas {
|
|
|
113
118
|
}
|
|
114
119
|
tx /= se.targetTouches.length
|
|
115
120
|
ty /= se.targetTouches.length
|
|
116
|
-
|
|
121
|
+
|
|
122
|
+
// Adjust for container's offset
|
|
123
|
+
tx -= containerRect.left
|
|
124
|
+
ty -= containerRect.top
|
|
125
|
+
|
|
126
|
+
// Zoom at the average touch position
|
|
117
127
|
this.zoom(f, this.pixToGeoX(tx), this.pixToGeoY(ty))
|
|
118
128
|
}
|
|
119
129
|
}
|
package/src/core/Map.js
CHANGED
|
@@ -60,9 +60,10 @@ export class Map {
|
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
// legend div
|
|
63
|
-
this.
|
|
64
|
-
|
|
65
|
-
|
|
63
|
+
this.legend = opts.legendContainer
|
|
64
|
+
? select(opts.legendContainer) // Wrap the provided HTML node in a D3 selection
|
|
65
|
+
: null
|
|
66
|
+
if (!this.legend) this.initialiseLegend()
|
|
66
67
|
|
|
67
68
|
//tooltip
|
|
68
69
|
|
|
@@ -122,9 +123,9 @@ export class Map {
|
|
|
122
123
|
}
|
|
123
124
|
|
|
124
125
|
initialiseLegend() {
|
|
125
|
-
this.legend = select(this.container
|
|
126
|
-
.append('div')
|
|
127
|
-
.attr('id',
|
|
126
|
+
this.legend = select(this.container)
|
|
127
|
+
.append('div') // Create a new container
|
|
128
|
+
.attr('id', 'gridviz-legend')
|
|
128
129
|
.style('position', 'absolute')
|
|
129
130
|
.style('width', 'auto')
|
|
130
131
|
.style('height', 'auto')
|
|
@@ -432,35 +433,43 @@ export class Map {
|
|
|
432
433
|
* @memberof App
|
|
433
434
|
*/
|
|
434
435
|
defineResizeObserver() {
|
|
435
|
-
//
|
|
436
|
+
// Track whether the observer is currently processing a resize event
|
|
437
|
+
let resizePending = false
|
|
438
|
+
|
|
436
439
|
const resizeObserver = new ResizeObserver((entries) => {
|
|
440
|
+
if (!Array.isArray(entries) || !entries.length) return
|
|
441
|
+
|
|
437
442
|
let container = this.container
|
|
438
|
-
|
|
443
|
+
|
|
444
|
+
// Ensure the container has valid dimensions
|
|
439
445
|
if (container.clientWidth > 0 && container.clientHeight > 0) {
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
446
|
+
if (!resizePending) {
|
|
447
|
+
resizePending = true // Prevent overlapping resize triggers
|
|
448
|
+
|
|
449
|
+
window.requestAnimationFrame(() => {
|
|
450
|
+
resizePending = false // Reset the flag after processing
|
|
451
|
+
|
|
452
|
+
// Check for size changes
|
|
453
|
+
if (this.h !== container.clientHeight || this.w !== container.clientWidth) {
|
|
454
|
+
this.h = container.clientHeight
|
|
455
|
+
this.w = container.clientWidth
|
|
456
|
+
|
|
457
|
+
// Update geoCanvas sizes
|
|
458
|
+
this.geoCanvas.h = this.h
|
|
459
|
+
this.geoCanvas.w = this.w
|
|
460
|
+
this.geoCanvas.canvas.setAttribute('width', String(this.w))
|
|
461
|
+
this.geoCanvas.canvas.setAttribute('height', String(this.h))
|
|
462
|
+
this.geoCanvas.offscreenCanvas.setAttribute('width', String(this.w))
|
|
463
|
+
this.geoCanvas.offscreenCanvas.setAttribute('height', String(this.h))
|
|
464
|
+
|
|
465
|
+
this.redraw()
|
|
466
|
+
|
|
467
|
+
// Optionally reposition UI elements
|
|
468
|
+
// if (this.zoomButtons) this.zoomButtons.node.style.left = this.w - 50 + 'px';
|
|
469
|
+
// if (this.fullscreenButton) this.fullscreenButton.node.style.left = this.w - 50 + 'px';
|
|
470
|
+
}
|
|
471
|
+
})
|
|
472
|
+
}
|
|
464
473
|
}
|
|
465
474
|
})
|
|
466
475
|
|
package/src/core/Tooltip.js
CHANGED
|
@@ -16,8 +16,6 @@ export class Tooltip {
|
|
|
16
16
|
constructor(opts) {
|
|
17
17
|
opts = opts || {}
|
|
18
18
|
|
|
19
|
-
/** @type {string} */
|
|
20
|
-
this.div = opts.div || 'tooltip_eurostat'
|
|
21
19
|
/** @type {string} */
|
|
22
20
|
this.maxWidth = opts.maxWidth || '20em'
|
|
23
21
|
/** @type {string} */
|
|
@@ -47,21 +45,15 @@ export class Tooltip {
|
|
|
47
45
|
this.xMouseOffset = opts.xMouseOffset || 0
|
|
48
46
|
/** @type {HTMLElement} */
|
|
49
47
|
this.parentElement = opts.parentElement || document.body
|
|
48
|
+
/** @type {HTMLElement} */
|
|
49
|
+
this.tooltipElement = opts.tooltipElement || null
|
|
50
50
|
|
|
51
51
|
/**
|
|
52
52
|
* @public
|
|
53
53
|
* @type {import("d3-selection").Selection} */
|
|
54
|
-
this.tooltip =
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
//create tooltip DOM node
|
|
58
|
-
// this.tooltip = select(
|
|
59
|
-
// '#' + this.parentElement.id && this.parentElement.id != ''
|
|
60
|
-
// ? '#' + this.parentElement.id
|
|
61
|
-
// : 'body'
|
|
62
|
-
// )
|
|
63
|
-
this.tooltip = select('body').append('div').attr('id', this.div)
|
|
64
|
-
}
|
|
54
|
+
this.tooltip = opts.tooltipElement
|
|
55
|
+
? select(opts.tooltipElement) // Wrap the provided HTML node in a D3 selection
|
|
56
|
+
: select(this.parentElement).append('div').attr('id', 'gridviz-tooltip').attr('class', 'gridviz-tooltip') // create default element
|
|
65
57
|
|
|
66
58
|
//initialise
|
|
67
59
|
this.tooltip.style('max-width', this.maxWidth)
|
|
@@ -78,6 +70,10 @@ export class Tooltip {
|
|
|
78
70
|
this.tooltip.style('opacity', '0')
|
|
79
71
|
this.tooltip.style('text-wrap', 'nowrap')
|
|
80
72
|
|
|
73
|
+
// these placeholders are needed to prevent an infinite DOM resizeObserver loop:
|
|
74
|
+
this.tooltip.style('left', '0')
|
|
75
|
+
this.tooltip.style('top', '0')
|
|
76
|
+
|
|
81
77
|
// aria-labels (thanks to wahlatlas)
|
|
82
78
|
this.tooltip.attr('role', 'tooltip').attr('aria-live', 'polite')
|
|
83
79
|
}
|
|
@@ -107,14 +103,51 @@ export class Tooltip {
|
|
|
107
103
|
* @param {MouseEvent} event
|
|
108
104
|
*/
|
|
109
105
|
setPosition(event) {
|
|
106
|
+
// Get the bounding rect of the parent container (map2)
|
|
110
107
|
let parentRect = this.parentElement.getBoundingClientRect()
|
|
111
108
|
|
|
112
|
-
|
|
113
|
-
let
|
|
109
|
+
// Get the mouse position (relative to the parent container)
|
|
110
|
+
let x = event.clientX - parentRect.left + this.xOffset // Relative to parent
|
|
111
|
+
let y = event.clientY - parentRect.top - this.yOffset // Relative to parent
|
|
114
112
|
|
|
113
|
+
// Now, apply the position to the tooltip
|
|
115
114
|
this.tooltip.style('left', x + 'px').style('top', y + 'px')
|
|
116
115
|
|
|
117
|
-
|
|
116
|
+
// Ensure the tooltip stays inside the parent container
|
|
117
|
+
this.ensureTooltipInsideContainer(event, parentRect, this.tooltip.node())
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* @function ensureTooltipInsideContainer
|
|
121
|
+
* @description Prevents the tooltip from overflowing out of the App container (ensures that the tooltip is inside the gridviz container)
|
|
122
|
+
* @param {MouseEvent} event
|
|
123
|
+
* @param {DOMRect} parentRect
|
|
124
|
+
* @param {HTMLElement} tooltipNode
|
|
125
|
+
*/
|
|
126
|
+
ensureTooltipInsideContainer(event, parentRect, tooltipNode) {
|
|
127
|
+
let node = tooltipNode
|
|
128
|
+
let parentWidth = parentRect.width
|
|
129
|
+
let parentHeight = parentRect.height
|
|
130
|
+
|
|
131
|
+
// Ensure tooltip doesn't go beyond the right edge
|
|
132
|
+
if (node.offsetLeft + node.clientWidth > parentWidth) {
|
|
133
|
+
let left = event.clientX - node.clientWidth - this.xOffset
|
|
134
|
+
node.style.left = left + 'px'
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Ensure tooltip doesn't go beyond the bottom edge
|
|
138
|
+
if (node.offsetTop + node.clientHeight > parentHeight) {
|
|
139
|
+
node.style.top = parentHeight - node.clientHeight + 'px'
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Ensure tooltip doesn't go above the top edge
|
|
143
|
+
if (node.offsetTop < 0) {
|
|
144
|
+
node.style.top = 0 + 'px'
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Ensure tooltip doesn't go beyond the left edge
|
|
148
|
+
if (node.offsetLeft < 0) {
|
|
149
|
+
node.style.left = 0 + 'px'
|
|
150
|
+
}
|
|
118
151
|
}
|
|
119
152
|
|
|
120
153
|
/*
|
|
@@ -145,39 +178,4 @@ export class Tooltip {
|
|
|
145
178
|
this.tooltip.attr(k, v)
|
|
146
179
|
return this
|
|
147
180
|
}
|
|
148
|
-
|
|
149
|
-
/**
|
|
150
|
-
* @function ensureTooltipInsideContainer
|
|
151
|
-
* @description Prevents the tooltip from overflowing out of the App container (ensures that the tooltip is inside the gridviz container)
|
|
152
|
-
* @param {MouseEvent} event
|
|
153
|
-
* @param {DOMRect} parentRect
|
|
154
|
-
*/
|
|
155
|
-
ensureTooltipInsideContainer = function (event, parentRect) {
|
|
156
|
-
let node = this.tooltip.node()
|
|
157
|
-
let parentWidth = parentRect.width
|
|
158
|
-
let parentHeight = parentRect.height
|
|
159
|
-
|
|
160
|
-
//too far right
|
|
161
|
-
if (node.offsetLeft > parentRect.left + parentWidth - node.clientWidth) {
|
|
162
|
-
let left = event.x - node.clientWidth - this.xOffset
|
|
163
|
-
node.style.left = left + 'px'
|
|
164
|
-
// check if mouse covers tooltip
|
|
165
|
-
if (node.offsetLeft + node.clientWidth > event.x) {
|
|
166
|
-
//move tooltip left so it doesnt cover mouse
|
|
167
|
-
let left2 = event.x - node.clientWidth - this.xOffset
|
|
168
|
-
node.style.left = left2 + 'px'
|
|
169
|
-
}
|
|
170
|
-
// node.style.top = node.offsetTop + config.yOffset + "px";
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
//too far down
|
|
174
|
-
if (node.offsetTop + node.clientHeight > parentRect.top + parentHeight) {
|
|
175
|
-
node.style.top = node.offsetTop - node.clientHeight + 'px'
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
//too far up
|
|
179
|
-
if (node.offsetTop < parentRect.top) {
|
|
180
|
-
node.style.top = parentRect.top + this.yOffset + 'px'
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
181
|
}
|